accessibility_rs/engine/audit/
tree.rs

1use crate::engine::styles::layout::leaf;
2use accessibility_scraper::ElementRef;
3use accessibility_scraper::Html;
4use accessibility_tree::style::StyleSet;
5use std::collections::BTreeMap;
6use std::collections::HashSet;
7use taffy::prelude::*;
8
9lazy_static! {
10    static ref NODE_IGNORE: HashSet<&'static str> =
11        HashSet::from(["meta", "style", "link", "script", "head", "html", "body"]);
12}
13
14/// try to fix all possible issues using a spec against the tree.
15pub fn parse_accessibility_tree<'a, 'b, 'c>(
16    document: &'a Html,
17    _author: &StyleSet,
18) -> (
19    BTreeMap<&'a str, Vec<(ElementRef<'a>, Option<NodeId>)>>,
20    Option<TaffyTree>,
21) {
22    let mut accessibility_tree: BTreeMap<&str, Vec<(ElementRef<'_>, Option<NodeId>)>> =
23        BTreeMap::from(if document.root_element().value().name() == "html" {
24            [("title".into(), Default::default())]
25        } else {
26            [(Default::default(), Default::default())]
27        });
28    for node in document.tree.nodes() {
29        match ElementRef::wrap(node) {
30            Some(element) => {
31                let name = element.value().name();
32                accessibility_tree
33                    .entry(name)
34                    .and_modify(|n| n.push((element, None)))
35                    .or_insert(Vec::from([(element, None)]));
36            }
37            _ => (),
38        };
39    }
40
41    (accessibility_tree, None)
42}
43
44/// try to fix all possible issues using a spec against the tree with bounding boxs.
45pub fn parse_accessibility_tree_bounded<'a, 'b, 'c>(
46    document: &'a Html,
47    author: &StyleSet,
48) -> (
49    BTreeMap<&'a str, Vec<(ElementRef<'a>, Option<NodeId>)>>,
50    Option<TaffyTree>,
51) {
52    let mut taffy = TaffyTree::new();
53    let mut accessibility_tree: BTreeMap<&str, Vec<(ElementRef<'_>, Option<NodeId>)>> =
54        BTreeMap::from(if document.root_element().value().name() == "html" {
55            [("title".into(), Default::default())]
56        } else {
57            [(Default::default(), Default::default())]
58        });
59    let mut layout_leafs: Vec<NodeId> = vec![];
60
61    // push taffy layout in order from elements
62    for node in document.tree.nodes() {
63        match ElementRef::wrap(node) {
64            Some(element) => {
65                let name = element.value().name();
66                let layout_leaf = {
67                    if NODE_IGNORE.contains(name) {
68                        taffy.new_leaf(Default::default()).unwrap()
69                    } else {
70                        leaf(&element, &author, document, &mut taffy)
71                    }
72                };
73                accessibility_tree
74                    .entry(name)
75                    .and_modify(|n| n.push((element, Some(layout_leaf))))
76                    .or_insert(Vec::from([(element, Some(layout_leaf))]));
77            }
78            _ => (),
79        };
80    }
81
82    match accessibility_tree.get("body") {
83        Some(node) => {
84            for child in node[0].0.children() {
85                match ElementRef::wrap(child) {
86                    Some(element) => {
87                        if !NODE_IGNORE.contains(element.value().name()) {
88                            let leaf = leaf(&element, &author, document, &mut taffy);
89
90                            layout_leafs.push(leaf)
91                        }
92                    }
93                    _ => (),
94                }
95            }
96        }
97        _ => (),
98    };
99
100    let root_node = taffy
101        .new_with_children(
102            Style {
103                flex_direction: FlexDirection::Column,
104                // compute the default layout from CDP
105                size: Size {
106                    width: length(800.0),
107                    height: length(600.0),
108                },
109                ..Default::default()
110            },
111            &layout_leafs,
112        )
113        .unwrap();
114
115    taffy.compute_layout(root_node, Size::MAX_CONTENT).unwrap();
116
117    (accessibility_tree, Some(taffy))
118}