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