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