style/invalidation/element/
element_wrapper.rs1use crate::dom::TElement;
9use crate::selector_parser::{AttrValue, NonTSPseudoClass, PseudoElement, SelectorImpl};
10use crate::selector_parser::{Snapshot, SnapshotMap};
11use crate::values::AtomIdent;
12use crate::{CaseSensitivityExt, LocalName, Namespace, WeakAtom};
13use dom::ElementState;
14use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
15use selectors::bloom::BloomFilter;
16use selectors::matching::{ElementSelectorFlags, MatchingContext};
17use selectors::{Element, OpaqueElement};
18use std::cell::Cell;
19use std::fmt;
20
21pub trait ElementSnapshot: Sized {
42 fn state(&self) -> Option<ElementState>;
44
45 fn has_attrs(&self) -> bool;
47
48 fn debug_list_attributes(&self) -> String {
52 String::new()
53 }
54
55 fn id_attr(&self) -> Option<&WeakAtom>;
58
59 fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool;
62
63 fn is_part(&self, name: &AtomIdent) -> bool;
66
67 fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent>;
69
70 fn each_class<F>(&self, _: F)
73 where
74 F: FnMut(&AtomIdent);
75
76 fn has_custom_states(&self) -> bool;
78
79 fn has_custom_state(&self, state: &AtomIdent) -> bool;
81
82 fn each_custom_state<F>(&self, callback: F)
84 where
85 F: FnMut(&AtomIdent);
86
87 fn lang_attr(&self) -> Option<AttrValue>;
89}
90
91#[derive(Clone)]
94pub struct ElementWrapper<'a, E>
95where
96 E: TElement,
97{
98 element: E,
99 cached_snapshot: Cell<Option<&'a Snapshot>>,
100 snapshot_map: &'a SnapshotMap,
101}
102
103impl<'a, E> ElementWrapper<'a, E>
104where
105 E: TElement,
106{
107 pub fn new(el: E, snapshot_map: &'a SnapshotMap) -> Self {
109 ElementWrapper {
110 element: el,
111 cached_snapshot: Cell::new(None),
112 snapshot_map: snapshot_map,
113 }
114 }
115
116 pub fn snapshot(&self) -> Option<&'a Snapshot> {
118 if !self.element.has_snapshot() {
119 return None;
120 }
121
122 if let Some(s) = self.cached_snapshot.get() {
123 return Some(s);
124 }
125
126 let snapshot = self.snapshot_map.get(&self.element);
127 debug_assert!(snapshot.is_some(), "has_snapshot lied!");
128
129 self.cached_snapshot.set(snapshot);
130
131 snapshot
132 }
133
134 pub fn state_changes(&self) -> ElementState {
136 let snapshot = match self.snapshot() {
137 Some(s) => s,
138 None => return ElementState::empty(),
139 };
140
141 match snapshot.state() {
142 Some(state) => state ^ self.element.state(),
143 None => ElementState::empty(),
144 }
145 }
146
147 fn get_lang(&self) -> Option<AttrValue> {
151 let mut current = self.clone();
152 loop {
153 let lang = match self.snapshot() {
154 Some(snapshot) if snapshot.has_attrs() => snapshot.lang_attr(),
155 _ => current.element.lang_attr(),
156 };
157 if lang.is_some() {
158 return lang;
159 }
160 current = current.parent_element()?;
161 }
162 }
163}
164
165impl<'a, E> fmt::Debug for ElementWrapper<'a, E>
166where
167 E: TElement,
168{
169 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
170 self.element.fmt(f)
172 }
173}
174
175impl<'a, E> Element for ElementWrapper<'a, E>
176where
177 E: TElement,
178{
179 type Impl = SelectorImpl;
180
181 fn match_non_ts_pseudo_class(
182 &self,
183 pseudo_class: &NonTSPseudoClass,
184 context: &mut MatchingContext<Self::Impl>,
185 ) -> bool {
186 match *pseudo_class {
189 NonTSPseudoClass::Link => {
195 return self.is_link() && context.visited_handling().matches_unvisited();
196 },
197 NonTSPseudoClass::Visited => {
198 return self.is_link() && context.visited_handling().matches_visited();
199 },
200
201 #[cfg(feature = "gecko")]
202 NonTSPseudoClass::MozTableBorderNonzero => {
203 if let Some(snapshot) = self.snapshot() {
204 if snapshot.has_other_pseudo_class_state() {
205 return snapshot.mIsTableBorderNonzero();
206 }
207 }
208 },
209
210 #[cfg(feature = "gecko")]
211 NonTSPseudoClass::MozSelectListBox => {
212 if let Some(snapshot) = self.snapshot() {
213 if snapshot.has_other_pseudo_class_state() {
214 return snapshot.mIsSelectListBox();
215 }
216 }
217 },
218
219 NonTSPseudoClass::Lang(ref lang_arg) => {
222 return self
223 .element
224 .match_element_lang(Some(self.get_lang()), lang_arg);
225 },
226
227 #[cfg(feature = "gecko")]
229 NonTSPseudoClass::Heading(ref levels) => {
230 return levels.matches_state(
231 self.snapshot()
232 .and_then(|s| s.state())
233 .unwrap_or_else(|| self.element.state()),
234 );
235 },
236
237 NonTSPseudoClass::CustomState(ref state) => return self.has_custom_state(&state.0),
239
240 _ => {},
241 }
242
243 let flag = pseudo_class.state_flag();
244 if flag.is_empty() {
245 return self
246 .element
247 .match_non_ts_pseudo_class(pseudo_class, context);
248 }
249 match self.snapshot().and_then(|s| s.state()) {
250 Some(snapshot_state) => snapshot_state.intersects(flag),
251 None => self
252 .element
253 .match_non_ts_pseudo_class(pseudo_class, context),
254 }
255 }
256
257 fn apply_selector_flags(&self, _flags: ElementSelectorFlags) {
258 debug_assert!(false, "Shouldn't need selector flags for invalidation");
259 }
260
261 fn match_pseudo_element(
262 &self,
263 pseudo_element: &PseudoElement,
264 context: &mut MatchingContext<Self::Impl>,
265 ) -> bool {
266 self.element.match_pseudo_element(pseudo_element, context)
267 }
268
269 fn is_link(&self) -> bool {
270 match self.snapshot().and_then(|s| s.state()) {
271 Some(state) => state.intersects(ElementState::VISITED_OR_UNVISITED),
272 None => self.element.is_link(),
273 }
274 }
275
276 fn opaque(&self) -> OpaqueElement {
277 self.element.opaque()
278 }
279
280 fn parent_element(&self) -> Option<Self> {
281 let parent = self.element.parent_element()?;
282 Some(Self::new(parent, self.snapshot_map))
283 }
284
285 fn parent_node_is_shadow_root(&self) -> bool {
286 self.element.parent_node_is_shadow_root()
287 }
288
289 fn containing_shadow_host(&self) -> Option<Self> {
290 let host = self.element.containing_shadow_host()?;
291 Some(Self::new(host, self.snapshot_map))
292 }
293
294 fn prev_sibling_element(&self) -> Option<Self> {
295 let sibling = self.element.prev_sibling_element()?;
296 Some(Self::new(sibling, self.snapshot_map))
297 }
298
299 fn next_sibling_element(&self) -> Option<Self> {
300 let sibling = self.element.next_sibling_element()?;
301 Some(Self::new(sibling, self.snapshot_map))
302 }
303
304 fn first_element_child(&self) -> Option<Self> {
305 let child = self.element.first_element_child()?;
306 Some(Self::new(child, self.snapshot_map))
307 }
308
309 #[inline]
310 fn is_html_element_in_html_document(&self) -> bool {
311 self.element.is_html_element_in_html_document()
312 }
313
314 #[inline]
315 fn is_html_slot_element(&self) -> bool {
316 self.element.is_html_slot_element()
317 }
318
319 #[inline]
320 fn has_local_name(
321 &self,
322 local_name: &<Self::Impl as ::selectors::SelectorImpl>::BorrowedLocalName,
323 ) -> bool {
324 self.element.has_local_name(local_name)
325 }
326
327 #[inline]
328 fn has_namespace(
329 &self,
330 ns: &<Self::Impl as ::selectors::SelectorImpl>::BorrowedNamespaceUrl,
331 ) -> bool {
332 self.element.has_namespace(ns)
333 }
334
335 #[inline]
336 fn is_same_type(&self, other: &Self) -> bool {
337 self.element.is_same_type(&other.element)
338 }
339
340 fn attr_matches(
341 &self,
342 ns: &NamespaceConstraint<&Namespace>,
343 local_name: &LocalName,
344 operation: &AttrSelectorOperation<&AttrValue>,
345 ) -> bool {
346 match self.snapshot() {
347 Some(snapshot) if snapshot.has_attrs() => {
348 snapshot.attr_matches(ns, local_name, operation)
349 },
350 _ => self.element.attr_matches(ns, local_name, operation),
351 }
352 }
353
354 fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
355 match self.snapshot() {
356 Some(snapshot) if snapshot.has_attrs() => snapshot
357 .id_attr()
358 .map_or(false, |atom| case_sensitivity.eq_atom(&atom, id)),
359 _ => self.element.has_id(id, case_sensitivity),
360 }
361 }
362
363 fn is_part(&self, name: &AtomIdent) -> bool {
364 match self.snapshot() {
365 Some(snapshot) if snapshot.has_attrs() => snapshot.is_part(name),
366 _ => self.element.is_part(name),
367 }
368 }
369
370 fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent> {
371 match self.snapshot() {
372 Some(snapshot) if snapshot.has_attrs() => snapshot.imported_part(name),
373 _ => self.element.imported_part(name),
374 }
375 }
376
377 fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
378 match self.snapshot() {
379 Some(snapshot) if snapshot.has_attrs() => snapshot.has_class(name, case_sensitivity),
380 _ => self.element.has_class(name, case_sensitivity),
381 }
382 }
383
384 fn has_custom_state(&self, state: &AtomIdent) -> bool {
385 match self.snapshot() {
386 Some(snapshot) if snapshot.has_custom_states() => snapshot.has_custom_state(state),
387 _ => self.element.has_custom_state(state),
388 }
389 }
390
391 fn is_empty(&self) -> bool {
392 self.element.is_empty()
393 }
394
395 fn is_root(&self) -> bool {
396 self.element.is_root()
397 }
398
399 fn is_pseudo_element(&self) -> bool {
400 self.element.is_pseudo_element()
401 }
402
403 fn pseudo_element_originating_element(&self) -> Option<Self> {
404 self.element
405 .pseudo_element_originating_element()
406 .map(|e| ElementWrapper::new(e, self.snapshot_map))
407 }
408
409 fn assigned_slot(&self) -> Option<Self> {
410 self.element
411 .assigned_slot()
412 .map(|e| ElementWrapper::new(e, self.snapshot_map))
413 }
414
415 fn add_element_unique_hashes(&self, _filter: &mut BloomFilter) -> bool {
416 false
418 }
419}