accessibility_tree/dom/
html.rs

1use 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                // Append to an existing Text node if we have one.
41                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        // self.new_node(NodeData::Comment {
128        //     _contents: text.into(),
129        // })
130    }
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}