scraper/element_ref/
element.rs1use fast_html5ever::Namespace;
2use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
3use selectors::matching;
4use selectors::{Element, OpaqueElement};
5
6use super::super::selector::{CssLocalName, CssString, NonTSPseudoClass, PseudoElement, Simple};
7use super::ElementRef;
8
9impl<'a> Element for ElementRef<'a> {
11 type Impl = Simple;
12
13 fn opaque(&self) -> OpaqueElement {
14 OpaqueElement::new(self.node.value())
15 }
16
17 fn parent_element(&self) -> Option<Self> {
18 self.parent().and_then(ElementRef::wrap)
19 }
20
21 fn parent_node_is_shadow_root(&self) -> bool {
22 false
23 }
24
25 fn containing_shadow_host(&self) -> Option<Self> {
26 None
27 }
28
29 fn first_element_child(&self) -> Option<Self> {
30 self.prev_siblings().nth(0).and_then(ElementRef::wrap)
31 }
32
33 fn apply_selector_flags(&self, _: selectors::matching::ElementSelectorFlags) {
34 }
36
37 fn is_pseudo_element(&self) -> bool {
38 false
39 }
40
41 fn is_part(&self, _name: &CssLocalName) -> bool {
42 false
43 }
44
45 fn is_same_type(&self, other: &Self) -> bool {
46 self.value().name == other.value().name
47 }
48
49 fn imported_part(&self, _: &CssLocalName) -> Option<CssLocalName> {
50 None
51 }
52
53 fn prev_sibling_element(&self) -> Option<Self> {
54 self.prev_siblings()
55 .find(|sibling| sibling.value().is_element())
56 .map(ElementRef::new)
57 }
58
59 fn next_sibling_element(&self) -> Option<Self> {
60 self.next_siblings()
61 .find(|sibling| sibling.value().is_element())
62 .map(ElementRef::new)
63 }
64
65 fn is_html_element_in_html_document(&self) -> bool {
66 self.value().name.ns == ns!(html)
68 }
69
70 fn has_local_name(&self, name: &CssLocalName) -> bool {
71 self.value().name.local == name.0
72 }
73
74 fn has_namespace(&self, namespace: &Namespace) -> bool {
75 &self.value().name.ns == namespace
76 }
77
78 fn attr_matches(
79 &self,
80 ns: &NamespaceConstraint<&Namespace>,
81 local_name: &CssLocalName,
82 operation: &AttrSelectorOperation<&CssString>,
83 ) -> bool {
84 self.value().attrs.iter().any(|(key, value)| {
85 !matches!(*ns, NamespaceConstraint::Specific(url) if *url != key.ns)
86 && local_name.0 == key.local
87 && operation.eval_str(value)
88 })
89 }
90
91 fn match_non_ts_pseudo_class(
92 &self,
93 _pc: &NonTSPseudoClass,
94 _context: &mut matching::MatchingContext<Self::Impl>,
95 ) -> bool {
96 false
97 }
98
99 fn match_pseudo_element(
100 &self,
101 _pe: &PseudoElement,
102 _context: &mut matching::MatchingContext<Self::Impl>,
103 ) -> bool {
104 false
105 }
106
107 fn is_link(&self) -> bool {
108 self.value().name() == "link"
109 }
110
111 fn is_html_slot_element(&self) -> bool {
112 true
113 }
114
115 fn has_id(&self, id: &CssLocalName, case_sensitivity: CaseSensitivity) -> bool {
116 match self.value().id {
117 Some(ref val) => case_sensitivity.eq(id.0.as_bytes(), val.as_bytes()),
118 None => false,
119 }
120 }
121
122 fn has_class(&self, name: &CssLocalName, case_sensitivity: CaseSensitivity) -> bool {
123 self.value().has_class(&name.0, case_sensitivity)
124 }
125
126 fn is_empty(&self) -> bool {
127 !self
128 .children()
129 .any(|child| child.value().is_element() || child.value().is_text())
130 }
131
132 fn is_root(&self) -> bool {
133 self.parent()
134 .map_or(false, |parent| parent.value().is_document())
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use crate::html::Html;
141 use crate::selector::{CssLocalName, Selector};
142 use selectors::attr::CaseSensitivity;
143 use selectors::Element;
144
145 #[test]
146 fn test_has_id() {
147 let html = "<p id='link_id_456'>hey there</p>";
148 let fragment = Html::parse_fragment(html);
149 let sel = Selector::parse("p").unwrap();
150
151 let element = fragment.select(&sel).next().unwrap();
152 assert!(element.has_id(
153 &CssLocalName::from("link_id_456"),
154 CaseSensitivity::CaseSensitive
155 ));
156
157 let html = "<p>hey there</p>";
158 let fragment = Html::parse_fragment(html);
159 let element = fragment.select(&sel).next().unwrap();
160 assert!(!element.has_id(
161 &CssLocalName::from("any_link_id"),
162 CaseSensitivity::CaseSensitive
163 ));
164 }
165
166 #[test]
167 fn test_is_link() {
168 let html = "<link href='https://www.example.com'>";
169 let fragment = Html::parse_fragment(html);
170 let sel = Selector::parse("link").unwrap();
171 let element = fragment.select(&sel).next().unwrap();
172 assert!(element.is_link());
173
174 let html = "<p>hey there</p>";
175 let fragment = Html::parse_fragment(html);
176 let sel = Selector::parse("p").unwrap();
177 let element = fragment.select(&sel).next().unwrap();
178 assert!(!element.is_link());
179 }
180
181 #[test]
182 fn test_has_class() {
183 let html = "<p class='my_class'>hey there</p>";
184 let fragment = Html::parse_fragment(html);
185 let sel = Selector::parse("p").unwrap();
186 let element = fragment.select(&sel).next().unwrap();
187 assert!(element.has_class(
188 &CssLocalName::from("my_class"),
189 CaseSensitivity::CaseSensitive
190 ));
191
192 let html = "<p>hey there</p>";
193 let fragment = Html::parse_fragment(html);
194 let sel = Selector::parse("p").unwrap();
195 let element = fragment.select(&sel).next().unwrap();
196 assert!(!element.has_class(
197 &CssLocalName::from("my_class"),
198 CaseSensitivity::CaseSensitive
199 ));
200 }
201}