1use std::hash::Hash;
6use std::sync::atomic::Ordering;
7use std::{fmt, slice};
8
9use atomic_refcell::{AtomicRef, AtomicRefMut};
10use embedder_traits::UntrustedNodeAddress;
11use html5ever::{LocalName, Namespace, local_name, ns};
12use js::jsapi::JSObject;
13use layout_api::wrapper_traits::{
14 LayoutNode, PseudoElementChain, ThreadSafeLayoutElement, ThreadSafeLayoutNode,
15};
16use layout_api::{LayoutDamage, LayoutNodeType, StyleData};
17use selectors::Element as _;
18use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
19use selectors::bloom::{BLOOM_HASH_MASK, BloomFilter};
20use selectors::matching::{ElementSelectorFlags, MatchingContext, VisitedHandlingMode};
21use selectors::sink::Push;
22use servo_arc::{Arc, ArcBorrow};
23use style::CaseSensitivityExt;
24use style::animation::AnimationSetKey;
25use style::applicable_declarations::ApplicableDeclarationBlock;
26use style::attr::AttrValue;
27use style::bloom::each_relevant_element_hash;
28use style::context::SharedStyleContext;
29use style::data::ElementData;
30use style::dom::{DomChildren, LayoutIterator, TDocument, TElement, TNode, TShadowRoot};
31use style::properties::{ComputedValues, PropertyDeclarationBlock};
32use style::selector_parser::{
33 AttrValue as SelectorAttrValue, Lang, NonTSPseudoClass, PseudoElement, RestyleDamage,
34 SelectorImpl, extended_filtering,
35};
36use style::shared_lock::Locked as StyleLocked;
37use style::stylesheets::scope_rule::ImplicitScopeRoot;
38use style::values::computed::{Display, Image};
39use style::values::specified::align::AlignFlags;
40use style::values::specified::box_::{DisplayInside, DisplayOutside};
41use style::values::{AtomIdent, AtomString};
42use stylo_atoms::Atom;
43use stylo_dom::ElementState;
44
45use crate::dom::attr::AttrHelpersForLayout;
46use crate::dom::bindings::inheritance::{
47 Castable, CharacterDataTypeId, DocumentFragmentTypeId, ElementTypeId, HTMLElementTypeId,
48 NodeTypeId, TextTypeId,
49};
50use crate::dom::bindings::root::LayoutDom;
51use crate::dom::characterdata::LayoutCharacterDataHelpers;
52use crate::dom::element::{Element, LayoutElementHelpers};
53use crate::dom::html::htmlslotelement::HTMLSlotElement;
54use crate::dom::node::{LayoutNodeHelpers, Node, NodeFlags};
55use crate::layout_dom::{ServoLayoutNode, ServoShadowRoot, ServoThreadSafeLayoutNode};
56
57#[derive(Clone, Copy, Eq, Hash, PartialEq)]
59pub struct ServoLayoutElement<'dom> {
60 element: LayoutDom<'dom, Element>,
62}
63
64impl fmt::Debug for ServoLayoutElement<'_> {
65 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66 write!(f, "<{}", self.element.local_name())?;
67 if let Some(id) = self.id() {
68 write!(f, " id={}", id)?;
69 }
70 write!(f, "> ({:#x})", self.as_node().opaque().0)
71 }
72}
73
74impl<'dom> ServoLayoutElement<'dom> {
75 pub(super) fn from_layout_js(el: LayoutDom<'dom, Element>) -> Self {
76 ServoLayoutElement { element: el }
77 }
78
79 pub(super) fn is_html_element(&self) -> bool {
80 self.element.is_html_element()
81 }
82
83 #[inline]
84 fn get_attr_enum(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> {
85 self.element.get_attr_for_layout(namespace, name)
86 }
87
88 #[inline]
89 fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&str> {
90 self.element.get_attr_val_for_layout(namespace, name)
91 }
92
93 fn get_style_data(&self) -> Option<&StyleData> {
94 self.as_node().style_data()
95 }
96
97 pub unsafe fn unset_snapshot_flags(&self) {
104 unsafe {
105 self.as_node()
106 .node
107 .set_flag(NodeFlags::HAS_SNAPSHOT | NodeFlags::HANDLED_SNAPSHOT, false);
108 }
109 }
110
111 pub unsafe fn set_has_snapshot(&self) {
118 unsafe {
119 self.as_node().node.set_flag(NodeFlags::HAS_SNAPSHOT, true);
120 }
121 }
122
123 fn is_body_element_of_html_element_root(&self) -> bool {
125 if self.element.local_name() != &local_name!("body") {
126 return false;
127 }
128
129 self.parent_element()
130 .map(|element| {
131 element.is_root() && element.element.local_name() == &local_name!("html")
132 })
133 .unwrap_or(false)
134 }
135
136 fn parent_element(&self) -> Option<Self> {
138 self.element
139 .upcast()
140 .composed_parent_node_ref()
141 .and_then(|node| node.downcast().map(ServoLayoutElement::from_layout_js))
142 }
143
144 fn is_root(&self) -> bool {
145 match self.as_node().parent_node() {
146 None => false,
147 Some(node) => matches!(node.script_type_id(), NodeTypeId::Document(_)),
148 }
149 }
150}
151
152pub enum DOMDescendantIterator<E>
153where
154 E: TElement,
155{
156 Children(DomChildren<E::ConcreteNode>),
159 Slottables { slot: E, index: usize },
161}
162
163impl<E> Iterator for DOMDescendantIterator<E>
164where
165 E: TElement,
166{
167 type Item = E::ConcreteNode;
168
169 fn next(&mut self) -> Option<Self::Item> {
170 match self {
171 Self::Children(children) => children.next(),
172 Self::Slottables { slot, index } => {
173 let slottables = slot.slotted_nodes();
174 let slot = slottables.get(*index)?;
175 *index += 1;
176 Some(*slot)
177 },
178 }
179 }
180}
181
182impl<'dom> style::dom::TElement for ServoLayoutElement<'dom> {
183 type ConcreteNode = ServoLayoutNode<'dom>;
184 type TraversalChildrenIterator = DOMDescendantIterator<Self>;
185
186 fn as_node(&self) -> ServoLayoutNode<'dom> {
187 ServoLayoutNode::from_layout_js(self.element.upcast())
188 }
189
190 fn traversal_children(&self) -> LayoutIterator<Self::TraversalChildrenIterator> {
191 let iterator = if self.slotted_nodes().is_empty() {
192 let children = if let Some(shadow_root) = self.shadow_root() {
193 shadow_root.as_node().dom_children()
194 } else {
195 self.as_node().dom_children()
196 };
197 DOMDescendantIterator::Children(children)
198 } else {
199 DOMDescendantIterator::Slottables {
200 slot: *self,
201 index: 0,
202 }
203 };
204
205 LayoutIterator(iterator)
206 }
207
208 fn traversal_parent(&self) -> Option<Self> {
209 self.as_node().traversal_parent()
210 }
211
212 fn inheritance_parent(&self) -> Option<Self> {
213 if self.is_pseudo_element() {
214 return self.pseudo_element_originating_element();
221 }
222
223 self.parent_element()
226 }
227
228 fn is_html_element(&self) -> bool {
229 ServoLayoutElement::is_html_element(self)
230 }
231
232 fn is_mathml_element(&self) -> bool {
233 *self.element.namespace() == ns!(mathml)
234 }
235
236 fn is_svg_element(&self) -> bool {
237 *self.element.namespace() == ns!(svg)
238 }
239
240 fn has_part_attr(&self) -> bool {
241 self.element
242 .get_attr_for_layout(&ns!(), &local_name!("part"))
243 .is_some()
244 }
245
246 fn exports_any_part(&self) -> bool {
247 self.element
248 .get_attr_for_layout(&ns!(), &local_name!("exportparts"))
249 .is_some()
250 }
251
252 fn style_attribute(&self) -> Option<ArcBorrow<'_, StyleLocked<PropertyDeclarationBlock>>> {
253 unsafe {
254 (*self.element.style_attribute())
255 .as_ref()
256 .map(|x| x.borrow_arc())
257 }
258 }
259
260 fn may_have_animations(&self) -> bool {
261 true
262 }
263
264 fn animation_rule(
265 &self,
266 context: &SharedStyleContext,
267 ) -> Option<Arc<StyleLocked<PropertyDeclarationBlock>>> {
268 let node = self.as_node();
269 let document = node.owner_doc();
270 context.animations.get_animation_declarations(
271 &AnimationSetKey::new_for_non_pseudo(node.opaque()),
272 context.current_time_for_animations,
273 document.style_shared_lock(),
274 )
275 }
276
277 fn transition_rule(
278 &self,
279 context: &SharedStyleContext,
280 ) -> Option<Arc<StyleLocked<PropertyDeclarationBlock>>> {
281 let node = self.as_node();
282 let document = node.owner_doc();
283 context.animations.get_transition_declarations(
284 &AnimationSetKey::new_for_non_pseudo(node.opaque()),
285 context.current_time_for_animations,
286 document.style_shared_lock(),
287 )
288 }
289
290 fn state(&self) -> ElementState {
291 self.element.get_state_for_layout()
292 }
293
294 #[inline]
295 fn id(&self) -> Option<&Atom> {
296 unsafe { (*self.element.id_attribute()).as_ref() }
297 }
298
299 #[inline(always)]
300 fn each_class<F>(&self, mut callback: F)
301 where
302 F: FnMut(&AtomIdent),
303 {
304 if let Some(classes) = self.element.get_classes_for_layout() {
305 for class in classes {
306 callback(AtomIdent::cast(class))
307 }
308 }
309 }
310
311 #[inline(always)]
312 fn each_attr_name<F>(&self, mut callback: F)
313 where
314 F: FnMut(&style::LocalName),
315 {
316 for attr in self.element.attrs() {
317 callback(style::values::GenericAtomIdent::cast(attr.local_name()))
318 }
319 }
320
321 fn each_part<F>(&self, mut callback: F)
322 where
323 F: FnMut(&AtomIdent),
324 {
325 if let Some(parts) = self.element.get_parts_for_layout() {
326 for part in parts {
327 callback(AtomIdent::cast(part))
328 }
329 }
330 }
331
332 fn each_exported_part<F>(&self, name: &AtomIdent, callback: F)
333 where
334 F: FnMut(&AtomIdent),
335 {
336 let Some(exported_parts) = self
337 .element
338 .get_attr_for_layout(&ns!(), &local_name!("exportparts"))
339 else {
340 return;
341 };
342 exported_parts
343 .as_shadow_parts()
344 .for_each_exported_part(AtomIdent::cast(name), callback);
345 }
346
347 fn has_dirty_descendants(&self) -> bool {
348 unsafe {
349 self.as_node()
350 .node
351 .get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS)
352 }
353 }
354
355 fn has_snapshot(&self) -> bool {
356 unsafe { self.as_node().node.get_flag(NodeFlags::HAS_SNAPSHOT) }
357 }
358
359 fn handled_snapshot(&self) -> bool {
360 unsafe { self.as_node().node.get_flag(NodeFlags::HANDLED_SNAPSHOT) }
361 }
362
363 unsafe fn set_handled_snapshot(&self) {
364 unsafe {
365 self.as_node()
366 .node
367 .set_flag(NodeFlags::HANDLED_SNAPSHOT, true);
368 }
369 }
370
371 unsafe fn set_dirty_descendants(&self) {
372 debug_assert!(self.as_node().is_connected());
373 unsafe {
374 self.as_node()
375 .node
376 .set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true)
377 }
378 }
379
380 unsafe fn unset_dirty_descendants(&self) {
381 unsafe {
382 self.as_node()
383 .node
384 .set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, false)
385 }
386 }
387
388 #[inline]
392 fn matches_user_and_content_rules(&self) -> bool {
393 !self.as_node().node.is_in_ua_widget()
394 }
395
396 #[inline]
399 fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
400 self.as_node().node.implemented_pseudo_element()
401 }
402
403 fn store_children_to_process(&self, n: isize) {
404 let data = self.get_style_data().unwrap();
405 data.parallel
406 .children_to_process
407 .store(n, Ordering::Relaxed);
408 }
409
410 fn did_process_child(&self) -> isize {
411 let data = self.get_style_data().unwrap();
412 let old_value = data
413 .parallel
414 .children_to_process
415 .fetch_sub(1, Ordering::Relaxed);
416 debug_assert!(old_value >= 1);
417 old_value - 1
418 }
419
420 unsafe fn clear_data(&self) {
421 unsafe { self.as_node().get_jsmanaged().clear_style_and_layout_data() }
422 }
423
424 unsafe fn ensure_data(&self) -> AtomicRefMut<'_, ElementData> {
425 unsafe {
426 self.as_node().get_jsmanaged().initialize_style_data();
427 };
428 self.mutate_data().unwrap()
429 }
430
431 fn has_data(&self) -> bool {
433 self.get_style_data().is_some()
434 }
435
436 fn borrow_data(&self) -> Option<AtomicRef<'_, ElementData>> {
438 self.get_style_data().map(|data| data.element_data.borrow())
439 }
440
441 fn mutate_data(&self) -> Option<AtomicRefMut<'_, ElementData>> {
443 self.get_style_data()
444 .map(|data| data.element_data.borrow_mut())
445 }
446
447 fn skip_item_display_fixup(&self) -> bool {
448 false
449 }
450
451 fn has_animations(&self, context: &SharedStyleContext) -> bool {
452 self.has_css_animations(context, None) ||
454 self.has_css_transitions(context, None)
455 }
456
457 fn has_css_animations(
458 &self,
459 context: &SharedStyleContext,
460 pseudo_element: Option<PseudoElement>,
461 ) -> bool {
462 let key = AnimationSetKey::new(self.as_node().opaque(), pseudo_element);
463 context.animations.has_active_animations(&key)
464 }
465
466 fn has_css_transitions(
467 &self,
468 context: &SharedStyleContext,
469 pseudo_element: Option<PseudoElement>,
470 ) -> bool {
471 let key = AnimationSetKey::new(self.as_node().opaque(), pseudo_element);
472 context.animations.has_active_transitions(&key)
473 }
474
475 #[inline]
476 fn lang_attr(&self) -> Option<SelectorAttrValue> {
477 self.get_attr(&ns!(xml), &local_name!("lang"))
478 .or_else(|| self.get_attr(&ns!(), &local_name!("lang")))
479 .map(|v| SelectorAttrValue::from(v as &str))
480 }
481
482 fn match_element_lang(
483 &self,
484 override_lang: Option<Option<SelectorAttrValue>>,
485 value: &Lang,
486 ) -> bool {
487 let element_lang = match override_lang {
501 Some(Some(lang)) => lang,
502 Some(None) => AtomString::default(),
503 None => AtomString::from(&*self.element.get_lang_for_layout()),
504 };
505 extended_filtering(&element_lang, value)
506 }
507
508 fn is_html_document_body_element(&self) -> bool {
509 self.is_body_element_of_html_element_root()
510 }
511
512 fn synthesize_presentational_hints_for_legacy_attributes<V>(
513 &self,
514 _visited_handling: VisitedHandlingMode,
515 hints: &mut V,
516 ) where
517 V: Push<ApplicableDeclarationBlock>,
518 {
519 self.element
520 .synthesize_presentational_hints_for_legacy_attributes(hints);
521 }
522
523 fn shadow_root(&self) -> Option<ServoShadowRoot<'dom>> {
525 self.element
526 .get_shadow_root_for_layout()
527 .map(ServoShadowRoot::from_layout_js)
528 }
529
530 fn containing_shadow(&self) -> Option<ServoShadowRoot<'dom>> {
532 self.element
533 .upcast()
534 .containing_shadow_root_for_layout()
535 .map(ServoShadowRoot::from_layout_js)
536 }
537
538 fn local_name(&self) -> &LocalName {
539 self.element.local_name()
540 }
541
542 fn namespace(&self) -> &Namespace {
543 self.element.namespace()
544 }
545
546 fn query_container_size(
547 &self,
548 _display: &Display,
549 ) -> euclid::default::Size2D<Option<app_units::Au>> {
550 todo!();
551 }
552
553 fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool {
554 self.element.get_selector_flags().contains(flags)
555 }
556
557 fn relative_selector_search_direction(&self) -> ElementSelectorFlags {
558 self.element
559 .get_selector_flags()
560 .intersection(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING)
561 }
562
563 fn each_custom_state<F>(&self, callback: F)
564 where
565 F: FnMut(&AtomIdent),
566 {
567 self.element.each_custom_state(callback);
568 }
569
570 fn implicit_scope_for_sheet_in_shadow_root(
572 opaque_host: ::selectors::OpaqueElement,
573 sheet_index: usize,
574 ) -> Option<ImplicitScopeRoot> {
575 let host = unsafe {
578 let ptr = opaque_host.as_const_ptr::<JSObject>();
579 let untrusted_address = UntrustedNodeAddress::from_id(ptr as usize);
580 let node = Node::from_untrusted_node_address(untrusted_address);
581 let trusted_address = node.to_trusted_node_address();
582 let servo_layout_node = ServoLayoutNode::new(&trusted_address);
583 servo_layout_node.as_element().unwrap()
584 };
585 host.shadow_root()?.implicit_scope_for_sheet(sheet_index)
586 }
587
588 fn slotted_nodes(&self) -> &[Self::ConcreteNode] {
589 let Some(slot_element) = self.element.unsafe_get().downcast::<HTMLSlotElement>() else {
590 return &[];
591 };
592 let assigned_nodes = slot_element.assigned_nodes();
593
594 unsafe {
599 slice::from_raw_parts(
600 assigned_nodes.as_ptr() as *const Self::ConcreteNode,
601 assigned_nodes.len(),
602 )
603 }
604 }
605
606 fn compute_layout_damage(old: &ComputedValues, new: &ComputedValues) -> RestyleDamage {
607 let box_tree_needs_rebuild = || {
608 let old_box = old.get_box();
609 let new_box = new.get_box();
610
611 if old_box.display != new_box.display ||
612 old_box.float != new_box.float ||
613 old_box.position != new_box.position
614 {
615 return true;
616 }
617
618 if new_box.position.is_absolutely_positioned() &&
619 old_box.original_display != new_box.original_display
620 {
621 return true;
622 }
623
624 if old.get_font() != new.get_font() {
625 return true;
626 }
627
628 if new_box.display.outside() == DisplayOutside::Block &&
632 new_box.display.inside() == DisplayInside::Flow
633 {
634 let alignment_establishes_new_block_formatting_context =
635 |style: &ComputedValues| {
636 style.get_position().align_content.0.primary() != AlignFlags::NORMAL
637 };
638
639 let old_column = old.get_column();
640 let new_column = new.get_column();
641 if old_box.overflow_x.is_scrollable() != new_box.overflow_x.is_scrollable() ||
642 old_column.is_multicol() != new_column.is_multicol() ||
643 old_column.column_span != new_column.column_span ||
644 alignment_establishes_new_block_formatting_context(old) !=
645 alignment_establishes_new_block_formatting_context(new)
646 {
647 return true;
648 }
649 }
650
651 if old_box.display.is_list_item() {
652 let old_list = old.get_list();
653 let new_list = new.get_list();
654 if old_list.list_style_position != new_list.list_style_position ||
655 old_list.list_style_image != new_list.list_style_image ||
656 (new_list.list_style_image == Image::None &&
657 old_list.list_style_type != new_list.list_style_type)
658 {
659 return true;
660 }
661 }
662
663 if new.is_pseudo_style() && old.get_counters().content != new.get_counters().content {
664 return true;
665 }
666
667 false
668 };
669
670 let text_shaping_needs_recollect = || {
671 if old.clone_direction() != new.clone_direction() ||
672 old.clone_unicode_bidi() != new.clone_unicode_bidi()
673 {
674 return true;
675 }
676
677 let old_text = old.get_inherited_text().clone();
678 let new_text = new.get_inherited_text().clone();
679 if old_text.white_space_collapse != new_text.white_space_collapse ||
680 old_text.text_transform != new_text.text_transform ||
681 old_text.word_break != new_text.word_break ||
682 old_text.overflow_wrap != new_text.overflow_wrap ||
683 old_text.letter_spacing != new_text.letter_spacing ||
684 old_text.word_spacing != new_text.word_spacing ||
685 old_text.text_rendering != new_text.text_rendering
686 {
687 return true;
688 }
689
690 false
691 };
692
693 if box_tree_needs_rebuild() {
694 RestyleDamage::from_bits_retain(LayoutDamage::REBUILD_BOX.bits())
695 } else if text_shaping_needs_recollect() {
696 RestyleDamage::from_bits_retain(LayoutDamage::RECOLLECT_BOX_TREE_CHILDREN.bits())
697 } else {
698 RestyleDamage::RELAYOUT
702 }
703 }
704}
705
706impl<'dom> ::selectors::Element for ServoLayoutElement<'dom> {
707 type Impl = SelectorImpl;
708
709 fn opaque(&self) -> ::selectors::OpaqueElement {
710 ::selectors::OpaqueElement::new(unsafe { &*(self.as_node().opaque().0 as *const ()) })
711 }
712
713 fn parent_element(&self) -> Option<Self> {
714 ServoLayoutElement::parent_element(self)
715 }
716
717 fn parent_node_is_shadow_root(&self) -> bool {
718 match self.as_node().parent_node() {
719 None => false,
720 Some(node) => {
721 node.script_type_id() ==
722 NodeTypeId::DocumentFragment(DocumentFragmentTypeId::ShadowRoot)
723 },
724 }
725 }
726
727 fn containing_shadow_host(&self) -> Option<Self> {
728 self.containing_shadow().map(|s| s.host())
729 }
730
731 #[inline]
732 fn is_pseudo_element(&self) -> bool {
733 self.implemented_pseudo_element().is_some()
734 }
735
736 #[inline]
737 fn pseudo_element_originating_element(&self) -> Option<Self> {
738 debug_assert!(self.is_pseudo_element());
739 debug_assert!(!self.matches_user_and_content_rules());
740 self.containing_shadow_host()
741 }
742
743 fn prev_sibling_element(&self) -> Option<Self> {
744 let mut node = self.as_node();
745 while let Some(sibling) = node.prev_sibling() {
746 if let Some(element) = sibling.as_element() {
747 return Some(element);
748 }
749 node = sibling;
750 }
751 None
752 }
753
754 fn next_sibling_element(&self) -> Option<ServoLayoutElement<'dom>> {
755 let mut node = self.as_node();
756 while let Some(sibling) = node.next_sibling() {
757 if let Some(element) = sibling.as_element() {
758 return Some(element);
759 }
760 node = sibling;
761 }
762 None
763 }
764
765 fn first_element_child(&self) -> Option<Self> {
766 self.as_node()
767 .dom_children()
768 .find_map(|child| child.as_element())
769 }
770
771 fn attr_matches(
772 &self,
773 ns: &NamespaceConstraint<&style::Namespace>,
774 local_name: &style::LocalName,
775 operation: &AttrSelectorOperation<&AtomString>,
776 ) -> bool {
777 match *ns {
778 NamespaceConstraint::Specific(ns) => self
779 .get_attr_enum(ns, local_name)
780 .is_some_and(|value| value.eval_selector(operation)),
781 NamespaceConstraint::Any => self
782 .element
783 .get_attr_vals_for_layout(local_name)
784 .iter()
785 .any(|value| value.eval_selector(operation)),
786 }
787 }
788
789 fn is_root(&self) -> bool {
790 ServoLayoutElement::is_root(self)
791 }
792
793 fn is_empty(&self) -> bool {
794 self.as_node()
795 .dom_children()
796 .all(|node| match node.script_type_id() {
797 NodeTypeId::Element(..) => false,
798 NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::Text)) => {
799 node.node.downcast().unwrap().data_for_layout().is_empty()
800 },
801 _ => true,
802 })
803 }
804
805 #[inline]
806 fn has_local_name(&self, name: &LocalName) -> bool {
807 self.element.local_name() == name
808 }
809
810 #[inline]
811 fn has_namespace(&self, ns: &Namespace) -> bool {
812 self.element.namespace() == ns
813 }
814
815 #[inline]
816 fn is_same_type(&self, other: &Self) -> bool {
817 self.element.local_name() == other.element.local_name() &&
818 self.element.namespace() == other.element.namespace()
819 }
820
821 fn match_non_ts_pseudo_class(
822 &self,
823 pseudo_class: &NonTSPseudoClass,
824 _: &mut MatchingContext<Self::Impl>,
825 ) -> bool {
826 match *pseudo_class {
827 NonTSPseudoClass::Link | NonTSPseudoClass::AnyLink => self.is_link(),
829 NonTSPseudoClass::Visited => false,
830
831 NonTSPseudoClass::CustomState(ref state) => self.has_custom_state(&state.0),
832 NonTSPseudoClass::Lang(ref lang) => self.match_element_lang(None, lang),
833
834 NonTSPseudoClass::ServoNonZeroBorder => !matches!(
835 self.element
836 .get_attr_for_layout(&ns!(), &local_name!("border")),
837 None | Some(&AttrValue::UInt(_, 0))
838 ),
839 NonTSPseudoClass::ReadOnly => !self
840 .element
841 .get_state_for_layout()
842 .contains(NonTSPseudoClass::ReadWrite.state_flag()),
843
844 NonTSPseudoClass::Active |
845 NonTSPseudoClass::Autofill |
846 NonTSPseudoClass::Checked |
847 NonTSPseudoClass::Default |
848 NonTSPseudoClass::Defined |
849 NonTSPseudoClass::Disabled |
850 NonTSPseudoClass::Enabled |
851 NonTSPseudoClass::Focus |
852 NonTSPseudoClass::FocusVisible |
853 NonTSPseudoClass::FocusWithin |
854 NonTSPseudoClass::Fullscreen |
855 NonTSPseudoClass::Hover |
856 NonTSPseudoClass::InRange |
857 NonTSPseudoClass::Indeterminate |
858 NonTSPseudoClass::Invalid |
859 NonTSPseudoClass::Modal |
860 NonTSPseudoClass::MozMeterOptimum |
861 NonTSPseudoClass::MozMeterSubOptimum |
862 NonTSPseudoClass::MozMeterSubSubOptimum |
863 NonTSPseudoClass::Optional |
864 NonTSPseudoClass::OutOfRange |
865 NonTSPseudoClass::PlaceholderShown |
866 NonTSPseudoClass::PopoverOpen |
867 NonTSPseudoClass::ReadWrite |
868 NonTSPseudoClass::Required |
869 NonTSPseudoClass::Target |
870 NonTSPseudoClass::UserInvalid |
871 NonTSPseudoClass::UserValid |
872 NonTSPseudoClass::Valid => self
873 .element
874 .get_state_for_layout()
875 .contains(pseudo_class.state_flag()),
876 }
877 }
878
879 fn match_pseudo_element(
880 &self,
881 pseudo: &PseudoElement,
882 _context: &mut MatchingContext<Self::Impl>,
883 ) -> bool {
884 self.implemented_pseudo_element() == Some(*pseudo)
885 }
886
887 #[inline]
888 fn is_link(&self) -> bool {
889 match self.as_node().script_type_id() {
890 NodeTypeId::Element(ElementTypeId::HTMLElement(
892 HTMLElementTypeId::HTMLAnchorElement,
893 )) |
894 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) |
895 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) => {
896 self.element
897 .get_attr_val_for_layout(&ns!(), &local_name!("href"))
898 .is_some()
899 },
900 _ => false,
901 }
902 }
903
904 #[inline]
905 fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
906 unsafe {
907 (*self.element.id_attribute())
908 .as_ref()
909 .is_some_and(|atom| case_sensitivity.eq_atom(atom, id))
910 }
911 }
912
913 #[inline]
914 fn is_part(&self, name: &AtomIdent) -> bool {
915 self.element.has_class_or_part_for_layout(
916 name,
917 &local_name!("part"),
918 CaseSensitivity::CaseSensitive,
919 )
920 }
921
922 fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent> {
923 self.element
924 .get_attr_for_layout(&ns!(), &local_name!("exportparts"))?
925 .as_shadow_parts()
926 .imported_part(name)
927 .map(|import| AtomIdent::new(import.clone()))
928 }
929
930 #[inline]
931 fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
932 self.element
933 .has_class_or_part_for_layout(name, &local_name!("class"), case_sensitivity)
934 }
935
936 fn is_html_slot_element(&self) -> bool {
937 self.element.is::<HTMLSlotElement>()
938 }
939
940 #[allow(unsafe_code)]
941 fn assigned_slot(&self) -> Option<Self> {
942 self.as_node().assigned_slot()
943 }
944
945 fn is_html_element_in_html_document(&self) -> bool {
946 self.element.is_html_element() && self.as_node().owner_doc().is_html_document()
947 }
948
949 fn apply_selector_flags(&self, flags: ElementSelectorFlags) {
950 let self_flags = flags.for_self();
952 if !self_flags.is_empty() {
953 self.element.insert_selector_flags(flags);
954 }
955
956 let parent_flags = flags.for_parent();
958 if !parent_flags.is_empty() {
959 if let Some(p) = self.as_node().parent_element() {
960 p.element.insert_selector_flags(flags);
961 }
962 }
963 }
964
965 fn add_element_unique_hashes(&self, filter: &mut BloomFilter) -> bool {
966 each_relevant_element_hash(*self, |hash| filter.insert_hash(hash & BLOOM_HASH_MASK));
967 true
968 }
969
970 fn has_custom_state(&self, name: &AtomIdent) -> bool {
971 let mut has_state = false;
972 self.element
973 .each_custom_state(|state| has_state |= state == name);
974
975 has_state
976 }
977}
978
979#[derive(Clone, Copy, Debug)]
982pub struct ServoThreadSafeLayoutElement<'dom> {
983 pub(super) element: ServoLayoutElement<'dom>,
985
986 pub(super) pseudo_element_chain: PseudoElementChain,
988}
989
990impl<'dom> ServoThreadSafeLayoutElement<'dom> {
991 pub fn shadow_root(&self) -> Option<ServoShadowRoot<'dom>> {
993 self.element
994 .element
995 .get_shadow_root_for_layout()
996 .map(ServoShadowRoot::from_layout_js)
997 }
998
999 pub fn slotted_nodes(&self) -> &[ServoLayoutNode<'dom>] {
1000 self.element.slotted_nodes()
1001 }
1002}
1003
1004impl<'dom> ThreadSafeLayoutElement<'dom> for ServoThreadSafeLayoutElement<'dom> {
1005 type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'dom>;
1006 type ConcreteElement = ServoLayoutElement<'dom>;
1007
1008 fn as_node(&self) -> ServoThreadSafeLayoutNode<'dom> {
1009 ServoThreadSafeLayoutNode {
1010 node: self.element.as_node(),
1011 pseudo_element_chain: self.pseudo_element_chain,
1012 }
1013 }
1014
1015 fn pseudo_element_chain(&self) -> PseudoElementChain {
1016 self.pseudo_element_chain
1017 }
1018
1019 fn with_pseudo(&self, pseudo_element: PseudoElement) -> Option<Self> {
1020 if pseudo_element.is_eager() &&
1021 self.style_data()
1022 .styles
1023 .pseudos
1024 .get(&pseudo_element)
1025 .is_none()
1026 {
1027 return None;
1028 }
1029
1030 if pseudo_element == PseudoElement::DetailsSummary &&
1031 (!self.has_local_name(&local_name!("details")) || !self.has_namespace(&ns!(html)))
1032 {
1033 return None;
1034 }
1035
1036 if pseudo_element == PseudoElement::DetailsContent &&
1037 (!self.has_local_name(&local_name!("details")) ||
1038 !self.has_namespace(&ns!(html)) ||
1039 self.get_attr(&ns!(), &local_name!("open")).is_none())
1040 {
1041 return None;
1042 }
1043
1044 if !self.pseudo_element_chain.is_empty() {
1046 assert!(!pseudo_element.is_eager());
1047 assert!(pseudo_element != PseudoElement::DetailsSummary);
1048 assert!(pseudo_element != PseudoElement::DetailsContent);
1049 }
1050
1051 Some(ServoThreadSafeLayoutElement {
1052 element: self.element,
1053 pseudo_element_chain: self.pseudo_element_chain.with_pseudo(pseudo_element),
1054 })
1055 }
1056
1057 fn type_id(&self) -> Option<LayoutNodeType> {
1058 self.as_node().type_id()
1059 }
1060
1061 fn unsafe_get(self) -> ServoLayoutElement<'dom> {
1062 self.element
1063 }
1064
1065 fn get_local_name(&self) -> &LocalName {
1066 self.element.local_name()
1067 }
1068
1069 fn get_attr_enum(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> {
1070 self.element.get_attr_enum(namespace, name)
1071 }
1072
1073 fn get_attr<'a>(&'a self, namespace: &Namespace, name: &LocalName) -> Option<&'a str> {
1074 self.element.get_attr(namespace, name)
1075 }
1076
1077 fn style_data(&self) -> AtomicRef<'_, ElementData> {
1078 self.element.borrow_data().expect("Unstyled layout node?")
1079 }
1080
1081 fn is_shadow_host(&self) -> bool {
1082 self.element.shadow_root().is_some()
1083 }
1084
1085 fn is_body_element_of_html_element_root(&self) -> bool {
1086 self.element.is_html_document_body_element()
1087 }
1088
1089 fn is_root(&self) -> bool {
1090 self.element.is_root()
1091 }
1092}
1093
1094impl ::selectors::Element for ServoThreadSafeLayoutElement<'_> {
1107 type Impl = SelectorImpl;
1108
1109 fn opaque(&self) -> ::selectors::OpaqueElement {
1110 ::selectors::OpaqueElement::new(unsafe { &*(self.as_node().opaque().0 as *const ()) })
1111 }
1112
1113 fn parent_element(&self) -> Option<Self> {
1114 warn!("ServoThreadSafeLayoutElement::parent_element called");
1115 None
1116 }
1117
1118 #[inline]
1119 fn parent_node_is_shadow_root(&self) -> bool {
1120 self.element.parent_node_is_shadow_root()
1121 }
1122
1123 #[inline]
1124 fn containing_shadow_host(&self) -> Option<Self> {
1125 self.element
1126 .containing_shadow_host()
1127 .and_then(|element| element.as_node().to_threadsafe().as_element())
1128 }
1129
1130 #[inline]
1131 fn is_pseudo_element(&self) -> bool {
1132 self.element.is_pseudo_element()
1133 }
1134
1135 #[inline]
1136 fn pseudo_element_originating_element(&self) -> Option<Self> {
1137 self.element
1138 .pseudo_element_originating_element()
1139 .and_then(|element| element.as_node().to_threadsafe().as_element())
1140 }
1141
1142 fn prev_sibling_element(&self) -> Option<Self> {
1144 warn!("ServoThreadSafeLayoutElement::prev_sibling_element called");
1145 None
1146 }
1147
1148 fn next_sibling_element(&self) -> Option<Self> {
1150 warn!("ServoThreadSafeLayoutElement::next_sibling_element called");
1151 None
1152 }
1153
1154 fn first_element_child(&self) -> Option<Self> {
1156 warn!("ServoThreadSafeLayoutElement::first_element_child called");
1157 None
1158 }
1159
1160 fn is_html_slot_element(&self) -> bool {
1161 self.element.is_html_slot_element()
1162 }
1163
1164 fn is_html_element_in_html_document(&self) -> bool {
1165 self.element.is_html_element_in_html_document()
1166 }
1167
1168 #[inline]
1169 fn has_local_name(&self, name: &LocalName) -> bool {
1170 self.element.local_name() == name
1171 }
1172
1173 #[inline]
1174 fn has_namespace(&self, ns: &Namespace) -> bool {
1175 self.element.namespace() == ns
1176 }
1177
1178 #[inline]
1179 fn is_same_type(&self, other: &Self) -> bool {
1180 self.element.local_name() == other.element.local_name() &&
1181 self.element.namespace() == other.element.namespace()
1182 }
1183
1184 fn attr_matches(
1185 &self,
1186 ns: &NamespaceConstraint<&style::Namespace>,
1187 local_name: &style::LocalName,
1188 operation: &AttrSelectorOperation<&AtomString>,
1189 ) -> bool {
1190 match *ns {
1191 NamespaceConstraint::Specific(ns) => self
1192 .get_attr_enum(ns, local_name)
1193 .is_some_and(|value| value.eval_selector(operation)),
1194 NamespaceConstraint::Any => self
1195 .element
1196 .element
1197 .get_attr_vals_for_layout(local_name)
1198 .iter()
1199 .any(|v| v.eval_selector(operation)),
1200 }
1201 }
1202
1203 fn match_non_ts_pseudo_class(
1204 &self,
1205 _: &NonTSPseudoClass,
1206 _: &mut MatchingContext<Self::Impl>,
1207 ) -> bool {
1208 warn!("ServoThreadSafeLayoutElement::match_non_ts_pseudo_class called");
1210 false
1211 }
1212
1213 fn match_pseudo_element(
1214 &self,
1215 pseudo: &PseudoElement,
1216 context: &mut MatchingContext<Self::Impl>,
1217 ) -> bool {
1218 self.element.match_pseudo_element(pseudo, context)
1219 }
1220
1221 fn is_link(&self) -> bool {
1222 warn!("ServoThreadSafeLayoutElement::is_link called");
1223 false
1224 }
1225
1226 fn has_id(&self, _id: &AtomIdent, _case_sensitivity: CaseSensitivity) -> bool {
1227 debug!("ServoThreadSafeLayoutElement::has_id called");
1228 false
1229 }
1230
1231 #[inline]
1232 fn is_part(&self, _name: &AtomIdent) -> bool {
1233 debug!("ServoThreadSafeLayoutElement::is_part called");
1234 false
1235 }
1236
1237 fn imported_part(&self, _: &AtomIdent) -> Option<AtomIdent> {
1238 debug!("ServoThreadSafeLayoutElement::imported_part called");
1239 None
1240 }
1241
1242 fn has_class(&self, _name: &AtomIdent, _case_sensitivity: CaseSensitivity) -> bool {
1243 debug!("ServoThreadSafeLayoutElement::has_class called");
1244 false
1245 }
1246
1247 fn is_empty(&self) -> bool {
1248 warn!("ServoThreadSafeLayoutElement::is_empty called");
1249 false
1250 }
1251
1252 fn is_root(&self) -> bool {
1253 warn!("ServoThreadSafeLayoutElement::is_root called");
1254 false
1255 }
1256
1257 fn apply_selector_flags(&self, flags: ElementSelectorFlags) {
1258 let self_flags = flags.for_self();
1260 if !self_flags.is_empty() {
1261 self.element.element.insert_selector_flags(flags);
1262 }
1263
1264 let parent_flags = flags.for_parent();
1266 if !parent_flags.is_empty() {
1267 if let Some(p) = self.element.parent_element() {
1268 p.element.insert_selector_flags(flags);
1269 }
1270 }
1271 }
1272
1273 fn add_element_unique_hashes(&self, filter: &mut BloomFilter) -> bool {
1274 each_relevant_element_hash(self.element, |hash| {
1275 filter.insert_hash(hash & BLOOM_HASH_MASK)
1276 });
1277 true
1278 }
1279
1280 fn has_custom_state(&self, _name: &AtomIdent) -> bool {
1281 false
1282 }
1283}