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
use html5ever::rcdom::{Node, NodeData};
use crate::pattern::Pattern;

fn is_multiple(tag_name: &str, attr_name: &str) -> bool {
    match (tag_name.to_lowercase().as_str(), attr_name.to_lowercase().as_str()) {
        (_, "class")                |
        (_, "accesskey")            |
        (_, "dropzone")             |
        ("a", "rel")                |
        ("a", "rev")                |
        ("link", "rel")             |
        ("link", "rev")             |
        ("tr", "headers")           |
        ("th", "headers")           |
        ("form", "accept-charset")  |
        ("object", "archive")       |
        ("area", "rel")             |
        ("icon", "sizes")           |
        ("iframe", "sandbox")       |
        ("output", "for")           => true,
        _ => false
    }
}

fn match_list_attr<V: Pattern>(needle: &V, haystack: &str) -> bool {
    for part in haystack.split(char::is_whitespace) {
        let part = part.trim().to_lowercase();
        if needle.matches(&part) {
            return true;
        }
    }
    false
}

pub(crate) fn list_aware_match<K: Pattern, V: Pattern>(node: &Node, attr_name: &K, attr_value: &V) -> bool {
    match node.data {
        NodeData::Element { ref name, ref attrs, ..} => {
            let attrs = attrs.borrow();
            for attr in attrs.iter() {
                let k = attr.name.local.as_ref();
                let v = attr.value.as_ref();
                if attr_name.matches(k) {
                    if is_multiple(name.local.as_ref(), &k) {
                        if match_list_attr(attr_value, &v) {
                            return true;
                        }
                    } else {
                        if attr_value.matches(v) {
                            return true;
                        }
                    }
                }
            }
        },
        _ => (),
    }
    false
}