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