1#![expect(unsafe_code)]
6#![deny(missing_docs)]
7
8use std::hash::Hash;
9use std::slice;
10use std::sync::atomic::Ordering;
11
12use embedder_traits::UntrustedNodeAddress;
13use euclid::default::Size2D;
14use html5ever::{LocalName, Namespace, local_name, ns};
15use js::jsapi::JSObject;
16use layout_api::{DangerousStyleElement, LayoutDamage, LayoutNode};
17use script_bindings::root::DomRoot;
18use selectors::Element as _;
19use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
20use selectors::bloom::{BLOOM_HASH_MASK, BloomFilter};
21use selectors::matching::{ElementSelectorFlags, MatchingContext, VisitedHandlingMode};
22use selectors::sink::Push;
23use servo_arc::{Arc, ArcBorrow};
24use style::CaseSensitivityExt;
25use style::animation::AnimationSetKey;
26use style::applicable_declarations::ApplicableDeclarationBlock;
27use style::attr::AttrValue;
28use style::bloom::each_relevant_element_hash;
29use style::context::SharedStyleContext;
30use style::data::{ElementDataMut, ElementDataRef};
31use style::dom::{LayoutIterator, TDocument, TElement, TNode, TShadowRoot};
32use style::properties::{ComputedValues, PropertyDeclarationBlock};
33use style::selector_parser::{
34 AttrValue as SelectorAttrValue, Lang, NonTSPseudoClass, PseudoElement, RestyleDamage,
35 SelectorImpl, extended_filtering,
36};
37use style::shared_lock::Locked as StyleLocked;
38use style::stylesheets::scope_rule::ImplicitScopeRoot;
39use style::values::computed::{Display, Image};
40use style::values::generics::counters::{Content, ContentItem, GenericContentItems};
41use style::values::specified::align::AlignFlags;
42use style::values::specified::box_::{DisplayInside, DisplayOutside};
43use style::values::{AtomIdent, AtomString};
44use stylo_atoms::Atom;
45use stylo_dom::ElementState;
46
47use crate::dom::bindings::inheritance::{
48 CharacterDataTypeId, DocumentFragmentTypeId, ElementTypeId, HTMLElementTypeId, NodeTypeId,
49};
50use crate::dom::bindings::root::LayoutDom;
51use crate::dom::element::Element;
52use crate::dom::html::htmlslotelement::HTMLSlotElement;
53use crate::dom::node::{Node, NodeFlags};
54use crate::layout_dom::{
55 DOMDescendantIterator, ServoDangerousStyleNode, ServoDangerousStyleShadowRoot,
56 ServoLayoutDomTypeBundle, ServoLayoutElement, ServoLayoutNode,
57};
58
59#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
63pub struct ServoDangerousStyleElement<'dom> {
64 pub(crate) element: LayoutDom<'dom, Element>,
65}
66
67unsafe impl Send for ServoDangerousStyleElement<'_> {}
68unsafe impl Sync for ServoDangerousStyleElement<'_> {}
69
70impl<'dom> ServoDangerousStyleElement<'dom> {
71 pub(crate) fn rooted(self) -> DomRoot<Element> {
72 DomRoot::from_ref(unsafe { self.element.as_ref() })
73 }
74}
75
76impl<'dom> From<LayoutDom<'dom, Element>> for ServoDangerousStyleElement<'dom> {
77 fn from(element: LayoutDom<'dom, Element>) -> Self {
78 Self { element }
79 }
80}
81
82impl<'dom> DangerousStyleElement<'dom> for ServoDangerousStyleElement<'dom> {
83 type ConcreteTypeBundle = ServoLayoutDomTypeBundle<'dom>;
84
85 fn layout_element(&self) -> ServoLayoutElement<'dom> {
86 self.element.into()
87 }
88}
89
90impl<'dom> style::dom::TElement for ServoDangerousStyleElement<'dom> {
91 type ConcreteNode = ServoDangerousStyleNode<'dom>;
92 type TraversalChildrenIterator = DOMDescendantIterator<'dom>;
93
94 fn as_node(&self) -> ServoDangerousStyleNode<'dom> {
95 self.element.upcast().into()
96 }
97
98 fn traversal_children(&self) -> LayoutIterator<Self::TraversalChildrenIterator> {
99 let iterator = if self.slotted_nodes().is_empty() {
100 let children = if let Some(shadow_root) = self.shadow_root() {
101 shadow_root.as_node().dom_children()
102 } else {
103 self.as_node().dom_children()
104 };
105 DOMDescendantIterator::Children(children)
106 } else {
107 DOMDescendantIterator::Slottables {
108 slot: *self,
109 index: 0,
110 }
111 };
112
113 LayoutIterator(iterator)
114 }
115
116 fn traversal_parent(&self) -> Option<Self> {
117 self.as_node().traversal_parent()
118 }
119
120 fn inheritance_parent(&self) -> Option<Self> {
121 if self.is_pseudo_element() {
122 return self.pseudo_element_originating_element();
129 }
130
131 self.traversal_parent()
132 }
133
134 fn is_html_element(&self) -> bool {
135 self.element.is_html_element()
136 }
137
138 fn is_mathml_element(&self) -> bool {
139 *self.element.namespace() == ns!(mathml)
140 }
141
142 fn is_svg_element(&self) -> bool {
143 *self.element.namespace() == ns!(svg)
144 }
145
146 fn has_part_attr(&self) -> bool {
147 self.element
148 .get_attr_for_layout(&ns!(), &local_name!("part"))
149 .is_some()
150 }
151
152 fn exports_any_part(&self) -> bool {
153 self.element
154 .get_attr_for_layout(&ns!(), &local_name!("exportparts"))
155 .is_some()
156 }
157
158 fn style_attribute(&self) -> Option<ArcBorrow<'_, StyleLocked<PropertyDeclarationBlock>>> {
159 unsafe {
160 (*self.element.style_attribute())
161 .as_ref()
162 .map(|x| x.borrow_arc())
163 }
164 }
165
166 fn may_have_animations(&self) -> bool {
167 unsafe { self.as_node().node.get_flag(NodeFlags::HAS_EVER_ANIMATED) }
168 }
169
170 fn set_may_have_animations(&self) {
171 unsafe {
172 self.as_node()
173 .node
174 .set_flag(NodeFlags::HAS_EVER_ANIMATED, true)
175 }
176 }
177
178 fn animation_rule(
179 &self,
180 context: &SharedStyleContext,
181 ) -> Option<Arc<StyleLocked<PropertyDeclarationBlock>>> {
182 let node = self.as_node();
183 let document = node.owner_doc();
184 context.animations.get_animation_declarations(
185 &AnimationSetKey::new_for_non_pseudo(node.opaque()),
186 context.current_time_for_animations,
187 &document.shared_style_locks().author,
188 )
189 }
190
191 fn transition_rule(
192 &self,
193 context: &SharedStyleContext,
194 ) -> Option<Arc<StyleLocked<PropertyDeclarationBlock>>> {
195 let node = self.as_node();
196 let document = node.owner_doc();
197 context.animations.get_transition_declarations(
198 &AnimationSetKey::new_for_non_pseudo(node.opaque()),
199 context.current_time_for_animations,
200 &document.shared_style_locks().author,
201 )
202 }
203
204 fn state(&self) -> ElementState {
205 self.element.get_state_for_layout()
206 }
207
208 #[inline]
209 fn id(&self) -> Option<&Atom> {
210 unsafe { self.element.id_attribute_unguarded() }
212 }
213
214 #[inline(always)]
215 fn each_class<F>(&self, mut callback: F)
216 where
217 F: FnMut(&AtomIdent),
218 {
219 if let Some(classes) = self.element.get_classes_for_layout() {
220 for class in classes {
221 callback(AtomIdent::cast(class))
222 }
223 }
224 }
225
226 #[inline(always)]
227 fn each_attr_name<F>(&self, mut callback: F)
228 where
229 F: FnMut(&style::LocalName),
230 {
231 self.element
232 .each_attr_name_for_layout(|name| callback(style::values::GenericAtomIdent::cast(name)))
233 }
234
235 fn each_part<F>(&self, mut callback: F)
236 where
237 F: FnMut(&AtomIdent),
238 {
239 if let Some(parts) = self.element.get_parts_for_layout() {
240 for part in parts {
241 callback(AtomIdent::cast(part))
242 }
243 }
244 }
245
246 fn each_exported_part<F>(&self, name: &AtomIdent, callback: F)
247 where
248 F: FnMut(&AtomIdent),
249 {
250 let Some(exported_parts) = self
251 .element
252 .get_attr_for_layout(&ns!(), &local_name!("exportparts"))
253 else {
254 return;
255 };
256 exported_parts
257 .as_shadow_parts()
258 .for_each_exported_part(AtomIdent::cast(name), callback);
259 }
260
261 fn has_dirty_descendants(&self) -> bool {
262 unsafe {
263 self.as_node()
264 .node
265 .get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS)
266 }
267 }
268
269 fn has_snapshot(&self) -> bool {
270 unsafe { self.as_node().node.get_flag(NodeFlags::HAS_SNAPSHOT) }
271 }
272
273 fn handled_snapshot(&self) -> bool {
274 unsafe { self.as_node().node.get_flag(NodeFlags::HANDLED_SNAPSHOT) }
275 }
276
277 unsafe fn set_handled_snapshot(&self) {
278 unsafe {
279 self.as_node()
280 .node
281 .set_flag(NodeFlags::HANDLED_SNAPSHOT, true);
282 }
283 }
284
285 unsafe fn set_dirty_descendants(&self) {
286 let node = self.as_node();
287 unsafe {
288 debug_assert!(node.node.get_flag(NodeFlags::IS_CONNECTED));
289 node.node.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true)
290 }
291 }
292
293 unsafe fn unset_dirty_descendants(&self) {
294 unsafe {
295 self.as_node()
296 .node
297 .set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, false)
298 }
299 }
300
301 #[inline]
305 fn matches_user_and_content_rules(&self) -> bool {
306 !self.as_node().node.is_in_ua_widget()
307 }
308
309 #[inline]
312 fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
313 self.as_node().node.implemented_pseudo_element()
314 }
315
316 fn store_children_to_process(&self, n: isize) {
317 let data = self.element.style_data().unwrap();
318 data.parallel
319 .children_to_process
320 .store(n, Ordering::Relaxed);
321 }
322
323 fn did_process_child(&self) -> isize {
324 let data = self.element.style_data().unwrap();
325 let old_value = data
326 .parallel
327 .children_to_process
328 .fetch_sub(1, Ordering::Relaxed);
329 debug_assert!(old_value >= 1);
330 old_value - 1
331 }
332
333 unsafe fn clear_data(&self) {
334 unsafe { self.element.clear_style_data() };
335 unsafe { self.as_node().node.clear_layout_data() }
336 }
337
338 unsafe fn ensure_data(&self) -> ElementDataMut<'_> {
339 unsafe { self.element.initialize_style_data() };
340 self.mutate_data().unwrap()
341 }
342
343 fn has_data(&self) -> bool {
345 self.element.style_data().is_some()
346 }
347
348 fn borrow_data(&self) -> Option<ElementDataRef<'_>> {
350 self.element
351 .style_data()
352 .map(|data| data.element_data.borrow())
353 }
354
355 fn mutate_data(&self) -> Option<ElementDataMut<'_>> {
357 self.element
358 .style_data()
359 .map(|data| data.element_data.borrow_mut())
360 }
361
362 fn skip_item_display_fixup(&self) -> bool {
363 false
364 }
365
366 fn has_animations(&self, context: &SharedStyleContext) -> bool {
367 let key = AnimationSetKey::new_for_non_pseudo(self.as_node().opaque());
369 self.may_have_animations() &&
370 context
371 .animations
372 .has_active_animations_or_transitions(&key)
373 }
374
375 fn has_css_animations(
376 &self,
377 context: &SharedStyleContext,
378 pseudo_element: Option<PseudoElement>,
379 ) -> bool {
380 let key = AnimationSetKey::new(self.as_node().opaque(), pseudo_element);
381 self.may_have_animations() && context.animations.has_active_animations(&key)
382 }
383
384 fn has_css_transitions(
385 &self,
386 context: &SharedStyleContext,
387 pseudo_element: Option<PseudoElement>,
388 ) -> bool {
389 let key = AnimationSetKey::new(self.as_node().opaque(), pseudo_element);
390 self.may_have_animations() && context.animations.has_active_transitions(&key)
391 }
392
393 #[inline]
394 fn lang_attr(&self) -> Option<SelectorAttrValue> {
395 self.element
396 .get_attr_for_layout(&ns!(xml), &local_name!("lang"))
397 .or_else(|| {
398 self.element
399 .get_attr_for_layout(&ns!(), &local_name!("lang"))
400 })
401 .map(|v| SelectorAttrValue::from(v as &str))
402 }
403
404 fn match_element_lang(
405 &self,
406 override_lang: Option<Option<SelectorAttrValue>>,
407 value: &Lang,
408 ) -> bool {
409 let element_lang = match override_lang {
423 Some(Some(lang)) => lang,
424 Some(None) => AtomString::default(),
425 None => self.element.get_lang_for_layout(),
426 };
427 extended_filtering(&element_lang, value)
428 }
429
430 fn is_html_document_body_element(&self) -> bool {
431 self.element.is_body_element_of_html_element_root()
432 }
433
434 fn synthesize_presentational_hints_for_legacy_attributes<V>(
435 &self,
436 _visited_handling: VisitedHandlingMode,
437 hints: &mut V,
438 ) where
439 V: Push<ApplicableDeclarationBlock>,
440 {
441 self.element
442 .synthesize_presentational_hints_for_legacy_attributes(hints);
443 }
444
445 fn shadow_root(&self) -> Option<ServoDangerousStyleShadowRoot<'dom>> {
447 self.element.get_shadow_root_for_layout().map(Into::into)
448 }
449
450 fn containing_shadow(&self) -> Option<ServoDangerousStyleShadowRoot<'dom>> {
452 self.element
453 .upcast()
454 .containing_shadow_root_for_layout()
455 .map(Into::into)
456 }
457
458 fn local_name(&self) -> &LocalName {
459 self.element.local_name()
460 }
461
462 fn namespace(&self) -> &Namespace {
463 self.element.namespace()
464 }
465
466 fn query_container_size(&self, _display: &Display) -> Size2D<Option<app_units::Au>> {
467 todo!();
468 }
469
470 fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool {
471 self.element.get_selector_flags().contains(flags)
472 }
473
474 fn relative_selector_search_direction(&self) -> ElementSelectorFlags {
475 self.element
476 .get_selector_flags()
477 .intersection(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING)
478 }
479
480 fn each_custom_state<F>(&self, callback: F)
481 where
482 F: FnMut(&AtomIdent),
483 {
484 self.element.each_custom_state_for_layout(callback);
485 }
486
487 fn implicit_scope_for_sheet_in_shadow_root(
489 opaque_host: ::selectors::OpaqueElement,
490 sheet_index: usize,
491 ) -> Option<ImplicitScopeRoot> {
492 let host = unsafe {
495 let ptr = opaque_host.to_ptr().as_ptr() as *const JSObject;
496 let untrusted_address = UntrustedNodeAddress::from_id(ptr as usize);
497 let node = Node::from_untrusted_node_address(untrusted_address);
498 let trusted_address = node.to_trusted_node_address();
499 let servo_layout_node = ServoLayoutNode::new(&trusted_address);
500 servo_layout_node.as_element().unwrap()
501 };
502 host.shadow_root()?.implicit_scope_for_sheet(sheet_index)
503 }
504
505 fn slotted_nodes(&self) -> &[Self::ConcreteNode] {
506 let Some(slot_element) = self.element.downcast::<HTMLSlotElement>() else {
507 return &[];
508 };
509 let assigned_nodes = slot_element.unsafe_get().assigned_nodes();
510
511 unsafe {
516 slice::from_raw_parts(
517 assigned_nodes.as_ptr() as *const ServoDangerousStyleNode,
518 assigned_nodes.len(),
519 )
520 }
521 }
522
523 fn compute_layout_damage(old: &ComputedValues, new: &ComputedValues) -> RestyleDamage {
524 let box_tree_needs_rebuild = || {
525 let old_box = old.get_box();
526 let new_box = new.get_box();
527
528 if old_box.display != new_box.display ||
529 old_box.float != new_box.float ||
530 old_box.position != new_box.position
531 {
532 return true;
533 }
534
535 if new_box.position.is_absolutely_positioned() &&
536 old_box.original_display != new_box.original_display
537 {
538 let position = new.get_position();
542 if (position.top.is_auto() && position.bottom.is_auto()) ||
543 (position.left.is_auto() && position.right.is_auto())
544 {
545 return true;
546 }
547 }
548
549 if old.get_font() != new.get_font() {
550 return true;
551 }
552
553 if old.get_position().order != new.get_position().order {
554 return true;
555 }
556
557 if matches!(
560 new.pseudo(),
561 Some(PseudoElement::Before | PseudoElement::After | PseudoElement::Marker),
562 ) && old.get_list().quotes != new.get_list().quotes
563 {
564 return true;
565 }
566
567 if new_box.display.outside() == DisplayOutside::Block &&
571 new_box.display.inside() == DisplayInside::Flow
572 {
573 let alignment_establishes_new_block_formatting_context =
574 |style: &ComputedValues| {
575 style.get_position().align_content.primary() != AlignFlags::NORMAL
576 };
577
578 let old_column = old.get_column();
579 let new_column = new.get_column();
580 if old_box.overflow_x.is_scrollable() != new_box.overflow_x.is_scrollable() ||
581 old_column.is_multicol() != new_column.is_multicol() ||
582 old_column.column_span != new_column.column_span ||
583 alignment_establishes_new_block_formatting_context(old) !=
584 alignment_establishes_new_block_formatting_context(new)
585 {
586 return true;
587 }
588 }
589
590 if old_box.display.is_list_item() {
591 let old_list = old.get_list();
592 let new_list = new.get_list();
593 if old_list.list_style_position != new_list.list_style_position ||
594 old_list.list_style_image != new_list.list_style_image ||
595 (new_list.list_style_image == Image::None &&
596 old_list.list_style_type != new_list.list_style_type)
597 {
598 return true;
599 }
600 }
601
602 if new.is_pseudo_style() && old.get_counters().content != new.get_counters().content {
603 return true;
604 }
605
606 fn replacement<Image>(content: &Content<Image>) -> Option<&Image> {
609 match content {
610 Content::Items(GenericContentItems { items, .. }) => match items.as_slice() {
611 [ContentItem::Image(image)] => Some(image),
612 _ => None,
613 },
614 _ => None,
615 }
616 }
617 if replacement(&old.get_counters().content) != replacement(&new.get_counters().content)
618 {
619 return true;
620 }
621
622 false
623 };
624
625 let text_shaping_needs_recollect = || {
626 if !old.direction_equals(new) || !old.unicode_bidi_equals(new) {
627 return true;
628 }
629
630 let old_text = old.get_inherited_text().clone();
631 let new_text = new.get_inherited_text().clone();
632 if old_text.white_space_collapse != new_text.white_space_collapse ||
633 old_text.text_transform != new_text.text_transform ||
634 old_text.word_break != new_text.word_break ||
635 old_text.overflow_wrap != new_text.overflow_wrap ||
636 old_text.letter_spacing != new_text.letter_spacing ||
637 old_text.word_spacing != new_text.word_spacing ||
638 old_text.text_rendering != new_text.text_rendering
639 {
640 return true;
641 }
642
643 false
644 };
645
646 if box_tree_needs_rebuild() {
647 RestyleDamage::from_bits_retain(LayoutDamage::BoxDamage.bits())
648 } else if text_shaping_needs_recollect() {
649 RestyleDamage::from_bits_retain(LayoutDamage::DescendantHasBoxDamage.bits())
650 } else {
651 RestyleDamage::RELAYOUT
655 }
656 }
657
658 fn get_attr(&self, attr: &style::LocalName, namespace: &style::Namespace) -> Option<String> {
659 if self.is_html_element_in_html_document() {
662 let attr = &style::LocalName::new(attr.to_ascii_lowercase());
663 self.element.get_attr_val_for_layout(namespace, attr)
664 } else {
665 self.element.get_attr_val_for_layout(namespace, attr)
666 }
667 .map(Into::into)
668 }
669}
670
671impl<'dom> ::selectors::Element for ServoDangerousStyleElement<'dom> {
672 type Impl = SelectorImpl;
673
674 fn opaque(&self) -> ::selectors::OpaqueElement {
675 ::selectors::OpaqueElement::new(unsafe { &*(self.as_node().opaque().0 as *const ()) })
676 }
677
678 fn parent_element(&self) -> Option<Self> {
679 self.as_node().parent_node()?.as_element()
680 }
681
682 fn parent_node_is_shadow_root(&self) -> bool {
683 self.as_node().parent_node().is_some_and(|parent_node| {
684 parent_node.node.type_id_for_layout() ==
685 NodeTypeId::DocumentFragment(DocumentFragmentTypeId::ShadowRoot)
686 })
687 }
688
689 fn containing_shadow_host(&self) -> Option<Self> {
690 self.containing_shadow()
691 .as_ref()
692 .map(ServoDangerousStyleShadowRoot::host)
693 }
694
695 #[inline]
696 fn is_pseudo_element(&self) -> bool {
697 self.implemented_pseudo_element().is_some()
698 }
699
700 #[inline]
701 fn pseudo_element_originating_element(&self) -> Option<Self> {
702 debug_assert!(self.is_pseudo_element());
703 debug_assert!(!self.matches_user_and_content_rules());
704 self.containing_shadow_host()
705 }
706
707 fn prev_sibling_element(&self) -> Option<Self> {
708 let mut node = self.as_node();
709 while let Some(sibling) = node.prev_sibling() {
710 if let Some(element) = sibling.as_element() {
711 return Some(element);
712 }
713 node = sibling;
714 }
715 None
716 }
717
718 fn next_sibling_element(&self) -> Option<ServoDangerousStyleElement<'dom>> {
719 let mut node = self.as_node();
720 while let Some(sibling) = node.next_sibling() {
721 if let Some(element) = sibling.as_element() {
722 return Some(element);
723 }
724 node = sibling;
725 }
726 None
727 }
728
729 fn first_element_child(&self) -> Option<Self> {
730 self.as_node()
731 .dom_children()
732 .find_map(|child| child.as_element())
733 }
734
735 fn attr_matches(
736 &self,
737 ns: &NamespaceConstraint<&style::Namespace>,
738 local_name: &style::LocalName,
739 operation: &AttrSelectorOperation<&AtomString>,
740 ) -> bool {
741 match *ns {
742 NamespaceConstraint::Specific(ns) => self
743 .element
744 .get_attr_for_layout(ns, local_name)
745 .is_some_and(|value| value.eval_selector(operation)),
746 NamespaceConstraint::Any => self
747 .element
748 .get_attr_vals_for_layout(local_name)
749 .any(|value| value.eval_selector(operation)),
750 }
751 }
752
753 fn is_root(&self) -> bool {
754 self.element.is_root()
755 }
756
757 fn is_empty(&self) -> bool {
758 self.as_node()
759 .dom_children()
760 .all(|node| match node.node.type_id_for_layout() {
761 NodeTypeId::Element(..) => false,
762 NodeTypeId::CharacterData(CharacterDataTypeId::Text(..)) => {
763 node.node.downcast().unwrap().data_for_layout().is_empty()
764 },
765 _ => true,
766 })
767 }
768
769 #[inline]
770 fn has_local_name(&self, name: &LocalName) -> bool {
771 self.element.local_name() == name
772 }
773
774 #[inline]
775 fn has_namespace(&self, ns: &Namespace) -> bool {
776 self.element.namespace() == ns
777 }
778
779 #[inline]
780 fn is_same_type(&self, other: &Self) -> bool {
781 self.element.local_name() == other.element.local_name() &&
782 self.element.namespace() == other.element.namespace()
783 }
784
785 fn match_non_ts_pseudo_class(
786 &self,
787 pseudo_class: &NonTSPseudoClass,
788 _: &mut MatchingContext<Self::Impl>,
789 ) -> bool {
790 match *pseudo_class {
791 NonTSPseudoClass::Link | NonTSPseudoClass::AnyLink => self.is_link(),
793 NonTSPseudoClass::Visited => false,
794
795 NonTSPseudoClass::CustomState(ref state) => self.has_custom_state(&state.0),
796 NonTSPseudoClass::Lang(ref lang) => self.match_element_lang(None, lang),
797
798 NonTSPseudoClass::ServoNonZeroBorder => !matches!(
799 self.element
800 .get_attr_for_layout(&ns!(), &local_name!("border")),
801 None | Some(&AttrValue::UInt(_, 0))
802 ),
803 NonTSPseudoClass::ReadOnly => !self
804 .element
805 .get_state_for_layout()
806 .contains(NonTSPseudoClass::ReadWrite.state_flag()),
807
808 NonTSPseudoClass::Active |
809 NonTSPseudoClass::Autofill |
810 NonTSPseudoClass::Checked |
811 NonTSPseudoClass::Default |
812 NonTSPseudoClass::Defined |
813 NonTSPseudoClass::Disabled |
814 NonTSPseudoClass::Enabled |
815 NonTSPseudoClass::Focus |
816 NonTSPseudoClass::FocusVisible |
817 NonTSPseudoClass::FocusWithin |
818 NonTSPseudoClass::Fullscreen |
819 NonTSPseudoClass::Hover |
820 NonTSPseudoClass::InRange |
821 NonTSPseudoClass::Indeterminate |
822 NonTSPseudoClass::Invalid |
823 NonTSPseudoClass::Modal |
824 NonTSPseudoClass::MozInert |
825 NonTSPseudoClass::MozMeterOptimum |
826 NonTSPseudoClass::MozMeterSubOptimum |
827 NonTSPseudoClass::MozMeterSubSubOptimum |
828 NonTSPseudoClass::Open |
829 NonTSPseudoClass::Optional |
830 NonTSPseudoClass::OutOfRange |
831 NonTSPseudoClass::PlaceholderShown |
832 NonTSPseudoClass::PopoverOpen |
833 NonTSPseudoClass::ReadWrite |
834 NonTSPseudoClass::Required |
835 NonTSPseudoClass::Target |
836 NonTSPseudoClass::UserInvalid |
837 NonTSPseudoClass::UserValid |
838 NonTSPseudoClass::Valid => self
839 .element
840 .get_state_for_layout()
841 .contains(pseudo_class.state_flag()),
842 }
843 }
844
845 fn match_pseudo_element(
846 &self,
847 pseudo: &PseudoElement,
848 _context: &mut MatchingContext<Self::Impl>,
849 ) -> bool {
850 self.implemented_pseudo_element() == Some(*pseudo)
851 }
852
853 #[inline]
854 fn is_link(&self) -> bool {
855 match self.as_node().node.type_id_for_layout() {
856 NodeTypeId::Element(ElementTypeId::HTMLElement(
858 HTMLElementTypeId::HTMLAnchorElement,
859 )) |
860 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) |
861 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) => {
862 self.element
863 .get_attr_val_for_layout(&ns!(), &local_name!("href"))
864 .is_some()
865 },
866 _ => false,
867 }
868 }
869
870 #[inline]
871 fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
872 self.element
873 .id_attribute()
874 .is_some_and(|atom| case_sensitivity.eq_atom(&atom, id))
875 }
876
877 #[inline]
878 fn is_part(&self, name: &AtomIdent) -> bool {
879 self.element.has_class_or_part_for_layout(
880 name,
881 &local_name!("part"),
882 CaseSensitivity::CaseSensitive,
883 )
884 }
885
886 fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent> {
887 self.element
888 .get_attr_for_layout(&ns!(), &local_name!("exportparts"))?
889 .as_shadow_parts()
890 .imported_part(name)
891 .map(|import| AtomIdent::new(import.clone()))
892 }
893
894 #[inline]
895 fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
896 self.element
897 .has_class_or_part_for_layout(name, &local_name!("class"), case_sensitivity)
898 }
899
900 fn is_html_slot_element(&self) -> bool {
901 self.element.is::<HTMLSlotElement>()
902 }
903
904 fn assigned_slot(&self) -> Option<Self> {
905 Some(
906 self.element
907 .upcast::<Node>()
908 .assigned_slot_for_layout()?
909 .upcast()
910 .into(),
911 )
912 }
913
914 fn is_html_element_in_html_document(&self) -> bool {
915 self.element.is_html_element() && self.as_node().owner_doc().is_html_document()
916 }
917
918 fn apply_selector_flags(&self, flags: ElementSelectorFlags) {
919 let self_flags = flags.for_self();
921 if !self_flags.is_empty() {
922 self.element.insert_selector_flags(flags);
923 }
924
925 let parent_flags = flags.for_parent();
927 if !parent_flags.is_empty() &&
928 let Some(p) = self.as_node().parent_element()
929 {
930 p.element.insert_selector_flags(flags);
931 }
932 }
933
934 fn add_element_unique_hashes(&self, filter: &mut BloomFilter) -> bool {
935 each_relevant_element_hash(*self, |hash| filter.insert_hash(hash & BLOOM_HASH_MASK));
936 true
937 }
938
939 fn has_custom_state(&self, name: &AtomIdent) -> bool {
940 let mut has_state = false;
941 self.element
942 .each_custom_state_for_layout(|state| has_state |= state == name);
943
944 has_state
945 }
946}