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