1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
use super::tree::parse_accessibility_tree;
use super::tree::parse_accessibility_tree_bounded;
use accessibility_scraper::ElementRef;
use accessibility_scraper::Html;
use accessibility_tree::style::StyleSet;
use markup5ever::local_name;
use slotmap::DefaultKey;
use taffy::Taffy;

/// The configuration for auditing
pub struct Auditor<'a> {
    /// the html document
    pub document: &'a Html,
    /// the tree to map to nodes
    pub tree: std::collections::BTreeMap<&'a str, Vec<(ElementRef<'a>, Option<DefaultKey>)>>,
    /// styles for the audit
    pub author: StyleSet,
    /// the matching context for css selectors
    pub match_context:
        selectors::matching::MatchingContext<'a, accessibility_scraper::selector::Simple>,
    /// layout handling
    pub taffy: Option<Taffy>,
}

impl<'a> Auditor<'a> {
    /// Create a new auditor that can be used to validate accessibility
    pub fn new(
        document: &'a Html,
        css_rules: &str,
        match_context: selectors::matching::MatchingContext<
            'a,
            accessibility_scraper::selector::Simple,
        >,
        bounds: bool,
    ) -> Auditor<'a> {
        // TODO: make stylesheet building optional and only on first requirement
        let author = {
            let mut author = accessibility_tree::style::StyleSetBuilder::new();
            if !css_rules.is_empty() {
                author.add_stylesheet(css_rules);
            } else {
                let selector =
                    unsafe { accessibility_scraper::Selector::parse("style").unwrap_unchecked() };
                let mut s = document.select(&selector);

                while let Some(node) = s.next() {
                    if let Some(type_attr) = node.attr(&local_name!("type")) {
                        if !type_attr.eq_ignore_ascii_case("text/css") {
                            continue;
                        }
                        author.add_stylesheet(&node.inner_html())
                    }
                }
            }
            author.finish()
        };

        let (tree, taffy, match_context) = if bounds {
            parse_accessibility_tree_bounded(&document, &author, match_context)
        } else {
            parse_accessibility_tree(&document, &author, match_context)
        };

        Auditor {
            document,
            tree,
            author,
            match_context,
            taffy,
        }
    }
}