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 tendril::StrTendril; |
17 | |
18 | pub use self::NodeOrText::{AppendNode, AppendText}; |
19 | pub use self::QuirksMode::{LimitedQuirks, NoQuirks, Quirks}; |
20 | |
21 | /// Something which can be inserted into the DOM. |
22 | /// |
23 | /// Adjacent sibling text nodes are merged into a single node, so |
24 | /// the sink may not want to allocate a `Handle` for each. |
25 | pub enum NodeOrText<Handle> { |
26 | AppendNode(Handle), |
27 | AppendText(StrTendril), |
28 | } |
29 | |
30 | /// A document's quirks mode, for compatibility with old browsers. See [quirks mode on wikipedia] |
31 | /// for more information. |
32 | /// |
33 | /// [quirks mode on wikipedia]: https://en.wikipedia.org/wiki/Quirks_mode |
34 | #[derive (PartialEq, Eq, Copy, Clone, Hash, Debug)] |
35 | pub enum QuirksMode { |
36 | /// Full quirks mode |
37 | Quirks, |
38 | /// Almost standards mode |
39 | LimitedQuirks, |
40 | /// Standards mode |
41 | NoQuirks, |
42 | } |
43 | |
44 | /// Whether to interrupt further parsing of the current input until |
45 | /// the next explicit resumption of the tokenizer, or continue without |
46 | /// any interruption. |
47 | #[derive (PartialEq, Eq, Copy, Clone, Hash, Debug)] |
48 | pub enum NextParserState { |
49 | /// Stop further parsing. |
50 | Suspend, |
51 | /// Continue without interruptions. |
52 | Continue, |
53 | } |
54 | |
55 | /// Special properties of an element, useful for tagging elements with this information. |
56 | #[derive (Default)] |
57 | pub struct ElementFlags { |
58 | /// A document fragment should be created, associated with the element, |
59 | /// and returned in TreeSink::get_template_contents. |
60 | /// |
61 | /// See [template-contents in the whatwg spec][whatwg template-contents]. |
62 | /// |
63 | /// [whatwg template-contents]: https://html.spec.whatwg.org/multipage/#template-contents |
64 | pub template: bool, |
65 | |
66 | /// This boolean should be recorded with the element and returned |
67 | /// in TreeSink::is_mathml_annotation_xml_integration_point |
68 | /// |
69 | /// See [html-integration-point in the whatwg spec][whatwg integration-point]. |
70 | /// |
71 | /// [whatwg integration-point]: https://html.spec.whatwg.org/multipage/#html-integration-point |
72 | pub mathml_annotation_xml_integration_point: bool, |
73 | |
74 | // Prevent construction from outside module |
75 | _private: (), |
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: &mut 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 | /// Methods a parser can use to create the DOM. The DOM provider implements this trait. |
103 | /// |
104 | /// Having this as a trait potentially allows multiple implementations of the DOM to be used with |
105 | /// the same parser. |
106 | pub trait TreeSink { |
107 | /// `Handle` is a reference to a DOM node. The tree builder requires |
108 | /// that a `Handle` implements `Clone` to get another reference to |
109 | /// the same node. |
110 | type Handle: Clone; |
111 | |
112 | /// The overall result of parsing. |
113 | /// |
114 | /// This should default to Self, but default associated types are not stable yet. |
115 | /// [rust-lang/rust#29661](https://github.com/rust-lang/rust/issues/29661) |
116 | type Output; |
117 | |
118 | /// Consume this sink and return the overall result of parsing. |
119 | /// |
120 | /// TODO:This should default to `fn finish(self) -> Self::Output { self }`, |
121 | /// but default associated types are not stable yet. |
122 | /// [rust-lang/rust#29661](https://github.com/rust-lang/rust/issues/29661) |
123 | fn finish(self) -> Self::Output; |
124 | |
125 | /// Signal a parse error. |
126 | fn parse_error(&mut self, msg: Cow<'static, str>); |
127 | |
128 | /// Get a handle to the `Document` node. |
129 | fn get_document(&mut self) -> Self::Handle; |
130 | |
131 | /// What is the name of this element? |
132 | /// |
133 | /// Should never be called on a non-element node; |
134 | /// feel free to `panic!`. |
135 | fn elem_name<'a>(&'a self, target: &'a Self::Handle) -> ExpandedName<'a>; |
136 | |
137 | /// Create an element. |
138 | /// |
139 | /// When creating a template element (`name.ns.expanded() == expanded_name!(html "template")`), |
140 | /// an associated document fragment called the "template contents" should |
141 | /// also be created. Later calls to self.get_template_contents() with that |
142 | /// given element return it. |
143 | /// See [the template element in the whatwg spec][whatwg template]. |
144 | /// |
145 | /// [whatwg template]: https://html.spec.whatwg.org/multipage/#the-template-element |
146 | fn create_element( |
147 | &mut self, |
148 | name: QualName, |
149 | attrs: Vec<Attribute>, |
150 | flags: ElementFlags, |
151 | ) -> Self::Handle; |
152 | |
153 | /// Create a comment node. |
154 | fn create_comment(&mut self, text: StrTendril) -> Self::Handle; |
155 | |
156 | /// Create a Processing Instruction node. |
157 | fn create_pi(&mut self, target: StrTendril, data: StrTendril) -> Self::Handle; |
158 | |
159 | /// Append a node as the last child of the given node. If this would |
160 | /// produce adjacent sibling text nodes, it should concatenate the text |
161 | /// instead. |
162 | /// |
163 | /// The child node will not already have a parent. |
164 | fn append(&mut self, parent: &Self::Handle, child: NodeOrText<Self::Handle>); |
165 | |
166 | /// When the insertion point is decided by the existence of a parent node of the |
167 | /// element, we consider both possibilities and send the element which will be used |
168 | /// if a parent node exists, along with the element to be used if there isn't one. |
169 | fn append_based_on_parent_node( |
170 | &mut self, |
171 | element: &Self::Handle, |
172 | prev_element: &Self::Handle, |
173 | child: NodeOrText<Self::Handle>, |
174 | ); |
175 | |
176 | /// Append a `DOCTYPE` element to the `Document` node. |
177 | fn append_doctype_to_document( |
178 | &mut self, |
179 | name: StrTendril, |
180 | public_id: StrTendril, |
181 | system_id: StrTendril, |
182 | ); |
183 | |
184 | /// Mark a HTML `<script>` as "already started". |
185 | fn mark_script_already_started(&mut self, _node: &Self::Handle) {} |
186 | |
187 | /// Indicate that a node was popped off the stack of open elements. |
188 | fn pop(&mut self, _node: &Self::Handle) {} |
189 | |
190 | /// Get a handle to a template's template contents. The tree builder |
191 | /// promises this will never be called with something else than |
192 | /// a template element. |
193 | fn get_template_contents(&mut self, target: &Self::Handle) -> Self::Handle; |
194 | |
195 | /// Do two handles refer to the same node? |
196 | fn same_node(&self, x: &Self::Handle, y: &Self::Handle) -> bool; |
197 | |
198 | /// Set the document's quirks mode. |
199 | fn set_quirks_mode(&mut self, mode: QuirksMode); |
200 | |
201 | /// Append a node as the sibling immediately before the given node. |
202 | /// |
203 | /// The tree builder promises that `sibling` is not a text node. However its |
204 | /// old previous sibling, which would become the new node's previous sibling, |
205 | /// could be a text node. If the new node is also a text node, the two should |
206 | /// be merged, as in the behavior of `append`. |
207 | /// |
208 | /// NB: `new_node` may have an old parent, from which it should be removed. |
209 | fn append_before_sibling(&mut self, sibling: &Self::Handle, new_node: NodeOrText<Self::Handle>); |
210 | |
211 | /// Add each attribute to the given element, if no attribute with that name |
212 | /// already exists. The tree builder promises this will never be called |
213 | /// with something else than an element. |
214 | fn add_attrs_if_missing(&mut self, target: &Self::Handle, attrs: Vec<Attribute>); |
215 | |
216 | /// Associate the given form-associatable element with the form element |
217 | fn associate_with_form( |
218 | &mut self, |
219 | _target: &Self::Handle, |
220 | _form: &Self::Handle, |
221 | _nodes: (&Self::Handle, Option<&Self::Handle>), |
222 | ) { |
223 | } |
224 | |
225 | /// Detach the given node from its parent. |
226 | fn remove_from_parent(&mut self, target: &Self::Handle); |
227 | |
228 | /// Remove all the children from node and append them to new_parent. |
229 | fn reparent_children(&mut self, node: &Self::Handle, new_parent: &Self::Handle); |
230 | |
231 | /// Returns true if the adjusted current node is an HTML integration point |
232 | /// and the token is a start tag. |
233 | fn is_mathml_annotation_xml_integration_point(&self, _handle: &Self::Handle) -> bool { |
234 | false |
235 | } |
236 | |
237 | /// Called whenever the line number changes. |
238 | fn set_current_line(&mut self, _line_number: u64) {} |
239 | |
240 | /// Indicate that a `script` element is complete. |
241 | fn complete_script(&mut self, _node: &Self::Handle) -> NextParserState { |
242 | NextParserState::Continue |
243 | } |
244 | } |
245 | |
246 | /// Trace hooks for a garbage-collected DOM. |
247 | pub trait Tracer { |
248 | type Handle; |
249 | |
250 | /// Upon a call to `trace_handles`, the tree builder will call this method |
251 | /// for each handle in its internal state. |
252 | fn trace_handle(&self, node: &Self::Handle); |
253 | } |
254 | |