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