accessibility_tree/dom/
html.rs1use super::*;
2use fast_html5ever::interface::tree_builder::{ElementFlags, NodeOrText, QuirksMode, TreeSink};
3use fast_html5ever::tendril::{StrTendril, TendrilSink};
4use fast_html5ever::{self, parse_document, ExpandedName};
5use std::borrow::Cow;
6use std::collections::HashSet;
7
8impl Document {
9 pub fn parse_html(utf8_bytes: &[u8]) -> Self {
10 let sink = Sink {
11 document: Document::new(),
12 quirks_mode: QuirksMode::NoQuirks,
13 };
14 parse_document(sink, Default::default())
15 .from_utf8()
16 .one(utf8_bytes)
17 }
18}
19
20struct Sink {
21 document: Document,
22 quirks_mode: QuirksMode,
23}
24
25impl Sink {
26 fn new_node(&mut self, data: NodeData) -> NodeId {
27 let node = Node::new(data);
28 let new_node = self.document.push_node(node);
29
30 new_node
31 }
32
33 fn append_common<P, A>(&mut self, child: NodeOrText<NodeId>, previous: P, append: A)
34 where
35 P: FnOnce(&mut Document) -> Option<NodeId>,
36 A: FnOnce(&mut Document, NodeId),
37 {
38 let new_node = match child {
39 NodeOrText::AppendText(text) => {
40 if let Some(id) = previous(&mut self.document) {
42 if let Node {
43 data: NodeData::Text { contents },
44 ..
45 } = &mut self.document[id]
46 {
47 contents.push_str(&text);
48 return;
49 }
50 }
51 self.new_node(NodeData::Text {
52 contents: text.into(),
53 })
54 }
55 NodeOrText::AppendNode(node) => node,
56 };
57
58 append(&mut self.document, new_node)
59 }
60}
61
62impl TreeSink for Sink {
63 type Handle = NodeId;
64 type Output = Document;
65
66 fn finish(self) -> Document {
67 self.document
68 }
69
70 fn parse_error(&mut self, _: Cow<'static, str>) {}
71
72 fn get_document(&mut self) -> NodeId {
73 Document::document_node_id()
74 }
75
76 fn set_quirks_mode(&mut self, mode: QuirksMode) {
77 self.quirks_mode = mode;
78 }
79
80 fn same_node(&self, x: &NodeId, y: &NodeId) -> bool {
81 x == y
82 }
83
84 fn elem_name<'a>(&'a self, &target: &'a NodeId) -> ExpandedName<'a> {
85 self.document[target]
86 .as_element()
87 .expect("not an element")
88 .name
89 .expanded()
90 }
91
92 fn get_template_contents(&mut self, &target: &NodeId) -> NodeId {
93 target
94 }
95
96 fn is_mathml_annotation_xml_integration_point(&self, &target: &NodeId) -> bool {
97 self.document[target]
98 .as_element()
99 .expect("not an element")
100 .mathml_annotation_xml_integration_point
101 }
102
103 fn create_element(
104 &mut self,
105 name: QualName,
106 attrs: Vec<fast_html5ever::Attribute>,
107 ElementFlags {
108 mathml_annotation_xml_integration_point,
109 ..
110 }: ElementFlags,
111 ) -> NodeId {
112 let is_style = name.expanded() == expanded_name!(html "style");
113 let element = self.new_node(NodeData::Element(ElementData {
114 name: name.clone(),
115 attrs: attrs.into_iter().map(Attribute::from).collect(),
116 mathml_annotation_xml_integration_point,
117 layout_data: Default::default(),
118 css_local_name: accessibility_scraper::selector::CssLocalName(name.local),
119 }));
120 if is_style {
121 self.document.style_elements.push(element)
122 }
123 element
124 }
125
126 fn create_comment(&mut self, _text: StrTendril) {
127 }
131
132 fn create_pi(&mut self, target: StrTendril, data: StrTendril) -> NodeId {
133 self.new_node(NodeData::ProcessingInstruction {
134 _target: target.into(),
135 _contents: data.into(),
136 })
137 }
138
139 fn append(&mut self, &parent: &NodeId, child: NodeOrText<NodeId>) {
140 self.append_common(
141 child,
142 |document| document[parent].last_child,
143 |document, new_node| document.append(parent, new_node),
144 )
145 }
146
147 fn append_before_sibling(&mut self, &sibling: &NodeId, child: NodeOrText<NodeId>) {
148 self.append_common(
149 child,
150 |document| document[sibling].previous_sibling,
151 |document, new_node| document.insert_before(sibling, new_node),
152 )
153 }
154
155 fn append_based_on_parent_node(
156 &mut self,
157 element: &NodeId,
158 prev_element: &NodeId,
159 child: NodeOrText<NodeId>,
160 ) {
161 if self.document[*element].parent.is_some() {
162 self.append_before_sibling(element, child)
163 } else {
164 self.append(prev_element, child)
165 }
166 }
167
168 fn append_doctype_to_document(
169 &mut self,
170 name: StrTendril,
171 public_id: StrTendril,
172 system_id: StrTendril,
173 ) {
174 let node = self.new_node(NodeData::Doctype {
175 _name: name.into(),
176 _public_id: public_id.into(),
177 _system_id: system_id.into(),
178 });
179 self.document.append(Document::document_node_id(), node)
180 }
181
182 fn add_attrs_if_missing(&mut self, &target: &NodeId, attrs: Vec<fast_html5ever::Attribute>) {
183 let element = if let NodeData::Element(element) = &mut self.document[target].data {
184 element
185 } else {
186 panic!("not an element")
187 };
188 let existing_names = element
189 .attrs
190 .iter()
191 .map(|e| e.name.clone())
192 .collect::<HashSet<_>>();
193 element.attrs.extend(
194 attrs
195 .into_iter()
196 .map(Attribute::from)
197 .filter(|attr| !existing_names.contains(&attr.name)),
198 );
199 }
200
201 fn remove_from_parent(&mut self, &target: &NodeId) {
202 self.document.detach(target)
203 }
204
205 fn reparent_children(&mut self, &node: &NodeId, &new_parent: &NodeId) {
206 let mut next_child = self.document[node].first_child;
207 while let Some(child) = next_child {
208 debug_assert_eq!(self.document[child].parent, Some(node));
209 self.document.append(new_parent, child);
210 next_child = self.document[child].next_sibling
211 }
212 }
213}
214
215impl From<fast_html5ever::Attribute> for Attribute {
216 fn from(attr: fast_html5ever::Attribute) -> Self {
217 Self {
218 name: attr.name,
219 value: attr.value.into(),
220 }
221 }
222}