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