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