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_for_layout(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 old.get_position().order != new.get_position().order {
627 return true;
628 }
629
630 if matches!(
633 new.pseudo(),
634 Some(PseudoElement::Before | PseudoElement::After | PseudoElement::Marker),
635 ) && old.get_list().quotes != new.get_list().quotes
636 {
637 return true;
638 }
639
640 if new_box.display.outside() == DisplayOutside::Block &&
644 new_box.display.inside() == DisplayInside::Flow
645 {
646 let alignment_establishes_new_block_formatting_context =
647 |style: &ComputedValues| {
648 style.get_position().align_content.primary() != AlignFlags::NORMAL
649 };
650
651 let old_column = old.get_column();
652 let new_column = new.get_column();
653 if old_box.overflow_x.is_scrollable() != new_box.overflow_x.is_scrollable() ||
654 old_column.is_multicol() != new_column.is_multicol() ||
655 old_column.column_span != new_column.column_span ||
656 alignment_establishes_new_block_formatting_context(old) !=
657 alignment_establishes_new_block_formatting_context(new)
658 {
659 return true;
660 }
661 }
662
663 if old_box.display.is_list_item() {
664 let old_list = old.get_list();
665 let new_list = new.get_list();
666 if old_list.list_style_position != new_list.list_style_position ||
667 old_list.list_style_image != new_list.list_style_image ||
668 (new_list.list_style_image == Image::None &&
669 old_list.list_style_type != new_list.list_style_type)
670 {
671 return true;
672 }
673 }
674
675 if new.is_pseudo_style() && old.get_counters().content != new.get_counters().content {
676 return true;
677 }
678
679 false
680 };
681
682 let text_shaping_needs_recollect = || {
683 if old.clone_direction() != new.clone_direction() ||
684 old.clone_unicode_bidi() != new.clone_unicode_bidi()
685 {
686 return true;
687 }
688
689 let old_text = old.get_inherited_text().clone();
690 let new_text = new.get_inherited_text().clone();
691 if old_text.white_space_collapse != new_text.white_space_collapse ||
692 old_text.text_transform != new_text.text_transform ||
693 old_text.word_break != new_text.word_break ||
694 old_text.overflow_wrap != new_text.overflow_wrap ||
695 old_text.letter_spacing != new_text.letter_spacing ||
696 old_text.word_spacing != new_text.word_spacing ||
697 old_text.text_rendering != new_text.text_rendering
698 {
699 return true;
700 }
701
702 false
703 };
704
705 if box_tree_needs_rebuild() {
706 RestyleDamage::from_bits_retain(LayoutDamage::REBUILD_BOX.bits())
707 } else if text_shaping_needs_recollect() {
708 RestyleDamage::from_bits_retain(LayoutDamage::RECOLLECT_BOX_TREE_CHILDREN.bits())
709 } else {
710 RestyleDamage::RELAYOUT
714 }
715 }
716}
717
718impl<'dom> ::selectors::Element for ServoLayoutElement<'dom> {
719 type Impl = SelectorImpl;
720
721 fn opaque(&self) -> ::selectors::OpaqueElement {
722 ::selectors::OpaqueElement::new(unsafe { &*(self.as_node().opaque().0 as *const ()) })
723 }
724
725 fn parent_element(&self) -> Option<Self> {
726 ServoLayoutElement::parent_element(self)
727 }
728
729 fn parent_node_is_shadow_root(&self) -> bool {
730 match self.as_node().parent_node() {
731 None => false,
732 Some(node) => {
733 node.script_type_id() ==
734 NodeTypeId::DocumentFragment(DocumentFragmentTypeId::ShadowRoot)
735 },
736 }
737 }
738
739 fn containing_shadow_host(&self) -> Option<Self> {
740 self.containing_shadow().map(|s| s.host())
741 }
742
743 #[inline]
744 fn is_pseudo_element(&self) -> bool {
745 self.implemented_pseudo_element().is_some()
746 }
747
748 #[inline]
749 fn pseudo_element_originating_element(&self) -> Option<Self> {
750 debug_assert!(self.is_pseudo_element());
751 debug_assert!(!self.matches_user_and_content_rules());
752 self.containing_shadow_host()
753 }
754
755 fn prev_sibling_element(&self) -> Option<Self> {
756 let mut node = self.as_node();
757 while let Some(sibling) = node.prev_sibling() {
758 if let Some(element) = sibling.as_element() {
759 return Some(element);
760 }
761 node = sibling;
762 }
763 None
764 }
765
766 fn next_sibling_element(&self) -> Option<ServoLayoutElement<'dom>> {
767 let mut node = self.as_node();
768 while let Some(sibling) = node.next_sibling() {
769 if let Some(element) = sibling.as_element() {
770 return Some(element);
771 }
772 node = sibling;
773 }
774 None
775 }
776
777 fn first_element_child(&self) -> Option<Self> {
778 self.as_node()
779 .dom_children()
780 .find_map(|child| child.as_element())
781 }
782
783 fn attr_matches(
784 &self,
785 ns: &NamespaceConstraint<&style::Namespace>,
786 local_name: &style::LocalName,
787 operation: &AttrSelectorOperation<&AtomString>,
788 ) -> bool {
789 match *ns {
790 NamespaceConstraint::Specific(ns) => self
791 .get_attr_enum(ns, local_name)
792 .is_some_and(|value| value.eval_selector(operation)),
793 NamespaceConstraint::Any => self
794 .element
795 .get_attr_vals_for_layout(local_name)
796 .any(|value| value.eval_selector(operation)),
797 }
798 }
799
800 fn is_root(&self) -> bool {
801 ServoLayoutElement::is_root(self)
802 }
803
804 fn is_empty(&self) -> bool {
805 self.as_node()
806 .dom_children()
807 .all(|node| match node.script_type_id() {
808 NodeTypeId::Element(..) => false,
809 NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::Text)) => {
810 node.node.downcast().unwrap().data_for_layout().is_empty()
811 },
812 _ => true,
813 })
814 }
815
816 #[inline]
817 fn has_local_name(&self, name: &LocalName) -> bool {
818 self.element.local_name() == name
819 }
820
821 #[inline]
822 fn has_namespace(&self, ns: &Namespace) -> bool {
823 self.element.namespace() == ns
824 }
825
826 #[inline]
827 fn is_same_type(&self, other: &Self) -> bool {
828 self.element.local_name() == other.element.local_name() &&
829 self.element.namespace() == other.element.namespace()
830 }
831
832 fn match_non_ts_pseudo_class(
833 &self,
834 pseudo_class: &NonTSPseudoClass,
835 _: &mut MatchingContext<Self::Impl>,
836 ) -> bool {
837 match *pseudo_class {
838 NonTSPseudoClass::Link | NonTSPseudoClass::AnyLink => self.is_link(),
840 NonTSPseudoClass::Visited => false,
841
842 NonTSPseudoClass::CustomState(ref state) => self.has_custom_state(&state.0),
843 NonTSPseudoClass::Lang(ref lang) => self.match_element_lang(None, lang),
844
845 NonTSPseudoClass::ServoNonZeroBorder => !matches!(
846 self.element
847 .get_attr_for_layout(&ns!(), &local_name!("border")),
848 None | Some(&AttrValue::UInt(_, 0))
849 ),
850 NonTSPseudoClass::ReadOnly => !self
851 .element
852 .get_state_for_layout()
853 .contains(NonTSPseudoClass::ReadWrite.state_flag()),
854
855 NonTSPseudoClass::Active |
856 NonTSPseudoClass::Autofill |
857 NonTSPseudoClass::Checked |
858 NonTSPseudoClass::Default |
859 NonTSPseudoClass::Defined |
860 NonTSPseudoClass::Disabled |
861 NonTSPseudoClass::Enabled |
862 NonTSPseudoClass::Focus |
863 NonTSPseudoClass::FocusVisible |
864 NonTSPseudoClass::FocusWithin |
865 NonTSPseudoClass::Fullscreen |
866 NonTSPseudoClass::Hover |
867 NonTSPseudoClass::InRange |
868 NonTSPseudoClass::Indeterminate |
869 NonTSPseudoClass::Invalid |
870 NonTSPseudoClass::Modal |
871 NonTSPseudoClass::MozMeterOptimum |
872 NonTSPseudoClass::MozMeterSubOptimum |
873 NonTSPseudoClass::MozMeterSubSubOptimum |
874 NonTSPseudoClass::Optional |
875 NonTSPseudoClass::OutOfRange |
876 NonTSPseudoClass::PlaceholderShown |
877 NonTSPseudoClass::PopoverOpen |
878 NonTSPseudoClass::ReadWrite |
879 NonTSPseudoClass::Required |
880 NonTSPseudoClass::Target |
881 NonTSPseudoClass::UserInvalid |
882 NonTSPseudoClass::UserValid |
883 NonTSPseudoClass::Valid => self
884 .element
885 .get_state_for_layout()
886 .contains(pseudo_class.state_flag()),
887 }
888 }
889
890 fn match_pseudo_element(
891 &self,
892 pseudo: &PseudoElement,
893 _context: &mut MatchingContext<Self::Impl>,
894 ) -> bool {
895 self.implemented_pseudo_element() == Some(*pseudo)
896 }
897
898 #[inline]
899 fn is_link(&self) -> bool {
900 match self.as_node().script_type_id() {
901 NodeTypeId::Element(ElementTypeId::HTMLElement(
903 HTMLElementTypeId::HTMLAnchorElement,
904 )) |
905 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) |
906 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) => {
907 self.element
908 .get_attr_val_for_layout(&ns!(), &local_name!("href"))
909 .is_some()
910 },
911 _ => false,
912 }
913 }
914
915 #[inline]
916 fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
917 unsafe {
918 (*self.element.id_attribute())
919 .as_ref()
920 .is_some_and(|atom| case_sensitivity.eq_atom(atom, id))
921 }
922 }
923
924 #[inline]
925 fn is_part(&self, name: &AtomIdent) -> bool {
926 self.element.has_class_or_part_for_layout(
927 name,
928 &local_name!("part"),
929 CaseSensitivity::CaseSensitive,
930 )
931 }
932
933 fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent> {
934 self.element
935 .get_attr_for_layout(&ns!(), &local_name!("exportparts"))?
936 .as_shadow_parts()
937 .imported_part(name)
938 .map(|import| AtomIdent::new(import.clone()))
939 }
940
941 #[inline]
942 fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
943 self.element
944 .has_class_or_part_for_layout(name, &local_name!("class"), case_sensitivity)
945 }
946
947 fn is_html_slot_element(&self) -> bool {
948 self.element.is::<HTMLSlotElement>()
949 }
950
951 fn assigned_slot(&self) -> Option<Self> {
952 self.as_node().assigned_slot()
953 }
954
955 fn is_html_element_in_html_document(&self) -> bool {
956 self.element.is_html_element() && self.as_node().owner_doc().is_html_document()
957 }
958
959 fn apply_selector_flags(&self, flags: ElementSelectorFlags) {
960 let self_flags = flags.for_self();
962 if !self_flags.is_empty() {
963 self.element.insert_selector_flags(flags);
964 }
965
966 let parent_flags = flags.for_parent();
968 if !parent_flags.is_empty() {
969 if let Some(p) = self.as_node().parent_element() {
970 p.element.insert_selector_flags(flags);
971 }
972 }
973 }
974
975 fn add_element_unique_hashes(&self, filter: &mut BloomFilter) -> bool {
976 each_relevant_element_hash(*self, |hash| filter.insert_hash(hash & BLOOM_HASH_MASK));
977 true
978 }
979
980 fn has_custom_state(&self, name: &AtomIdent) -> bool {
981 let mut has_state = false;
982 self.element
983 .each_custom_state_for_layout(|state| has_state |= state == name);
984
985 has_state
986 }
987}
988
989#[derive(Clone, Copy, Debug)]
992pub struct ServoThreadSafeLayoutElement<'dom> {
993 pub(super) element: ServoLayoutElement<'dom>,
995
996 pub(super) pseudo_element_chain: PseudoElementChain,
998}
999
1000impl<'dom> ServoThreadSafeLayoutElement<'dom> {
1001 pub fn shadow_root(&self) -> Option<ServoShadowRoot<'dom>> {
1003 self.element
1004 .element
1005 .get_shadow_root_for_layout()
1006 .map(ServoShadowRoot::from_layout_js)
1007 }
1008
1009 pub fn slotted_nodes(&self) -> &[ServoLayoutNode<'dom>] {
1010 self.element.slotted_nodes()
1011 }
1012}
1013
1014impl<'dom> ThreadSafeLayoutElement<'dom> for ServoThreadSafeLayoutElement<'dom> {
1015 type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'dom>;
1016 type ConcreteElement = ServoLayoutElement<'dom>;
1017
1018 fn as_node(&self) -> ServoThreadSafeLayoutNode<'dom> {
1019 ServoThreadSafeLayoutNode {
1020 node: self.element.as_node(),
1021 pseudo_element_chain: self.pseudo_element_chain,
1022 }
1023 }
1024
1025 fn pseudo_element_chain(&self) -> PseudoElementChain {
1026 self.pseudo_element_chain
1027 }
1028
1029 fn with_pseudo(&self, pseudo_element: PseudoElement) -> Option<Self> {
1030 if pseudo_element.is_eager() &&
1031 self.style_data()
1032 .styles
1033 .pseudos
1034 .get(&pseudo_element)
1035 .is_none()
1036 {
1037 return None;
1038 }
1039
1040 if pseudo_element == PseudoElement::DetailsSummary &&
1041 (!self.has_local_name(&local_name!("details")) || !self.has_namespace(&ns!(html)))
1042 {
1043 return None;
1044 }
1045
1046 if pseudo_element == PseudoElement::DetailsContent &&
1047 (!self.has_local_name(&local_name!("details")) ||
1048 !self.has_namespace(&ns!(html)) ||
1049 self.get_attr(&ns!(), &local_name!("open")).is_none())
1050 {
1051 return None;
1052 }
1053
1054 if !self.pseudo_element_chain.is_empty() {
1056 assert!(!pseudo_element.is_eager());
1057 assert!(pseudo_element != PseudoElement::DetailsSummary);
1058 assert!(pseudo_element != PseudoElement::DetailsContent);
1059 }
1060
1061 Some(ServoThreadSafeLayoutElement {
1062 element: self.element,
1063 pseudo_element_chain: self.pseudo_element_chain.with_pseudo(pseudo_element),
1064 })
1065 }
1066
1067 fn type_id(&self) -> Option<LayoutNodeType> {
1068 self.as_node().type_id()
1069 }
1070
1071 fn unsafe_get(self) -> ServoLayoutElement<'dom> {
1072 self.element
1073 }
1074
1075 fn get_local_name(&self) -> &LocalName {
1076 self.element.local_name()
1077 }
1078
1079 fn get_attr_enum(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> {
1080 self.element.get_attr_enum(namespace, name)
1081 }
1082
1083 fn get_attr<'a>(&'a self, namespace: &Namespace, name: &LocalName) -> Option<&'a str> {
1084 self.element.get_attr(namespace, name)
1085 }
1086
1087 fn style_data(&self) -> AtomicRef<'_, ElementData> {
1088 self.element.borrow_data().expect("Unstyled layout node?")
1089 }
1090
1091 fn is_shadow_host(&self) -> bool {
1092 self.element.shadow_root().is_some()
1093 }
1094
1095 fn is_body_element_of_html_element_root(&self) -> bool {
1096 self.element.is_html_document_body_element()
1097 }
1098
1099 fn is_root(&self) -> bool {
1100 self.element.is_root()
1101 }
1102}
1103
1104impl ::selectors::Element for ServoThreadSafeLayoutElement<'_> {
1117 type Impl = SelectorImpl;
1118
1119 fn opaque(&self) -> ::selectors::OpaqueElement {
1120 ::selectors::OpaqueElement::new(unsafe { &*(self.as_node().opaque().0 as *const ()) })
1121 }
1122
1123 fn parent_element(&self) -> Option<Self> {
1124 warn!("ServoThreadSafeLayoutElement::parent_element called");
1125 None
1126 }
1127
1128 #[inline]
1129 fn parent_node_is_shadow_root(&self) -> bool {
1130 self.element.parent_node_is_shadow_root()
1131 }
1132
1133 #[inline]
1134 fn containing_shadow_host(&self) -> Option<Self> {
1135 self.element
1136 .containing_shadow_host()
1137 .and_then(|element| element.as_node().to_threadsafe().as_element())
1138 }
1139
1140 #[inline]
1141 fn is_pseudo_element(&self) -> bool {
1142 self.element.is_pseudo_element()
1143 }
1144
1145 #[inline]
1146 fn pseudo_element_originating_element(&self) -> Option<Self> {
1147 self.element
1148 .pseudo_element_originating_element()
1149 .and_then(|element| element.as_node().to_threadsafe().as_element())
1150 }
1151
1152 fn prev_sibling_element(&self) -> Option<Self> {
1154 warn!("ServoThreadSafeLayoutElement::prev_sibling_element called");
1155 None
1156 }
1157
1158 fn next_sibling_element(&self) -> Option<Self> {
1160 warn!("ServoThreadSafeLayoutElement::next_sibling_element called");
1161 None
1162 }
1163
1164 fn first_element_child(&self) -> Option<Self> {
1166 warn!("ServoThreadSafeLayoutElement::first_element_child called");
1167 None
1168 }
1169
1170 fn is_html_slot_element(&self) -> bool {
1171 self.element.is_html_slot_element()
1172 }
1173
1174 fn is_html_element_in_html_document(&self) -> bool {
1175 self.element.is_html_element_in_html_document()
1176 }
1177
1178 #[inline]
1179 fn has_local_name(&self, name: &LocalName) -> bool {
1180 self.element.local_name() == name
1181 }
1182
1183 #[inline]
1184 fn has_namespace(&self, ns: &Namespace) -> bool {
1185 self.element.namespace() == ns
1186 }
1187
1188 #[inline]
1189 fn is_same_type(&self, other: &Self) -> bool {
1190 self.element.local_name() == other.element.local_name() &&
1191 self.element.namespace() == other.element.namespace()
1192 }
1193
1194 fn attr_matches(
1195 &self,
1196 ns: &NamespaceConstraint<&style::Namespace>,
1197 local_name: &style::LocalName,
1198 operation: &AttrSelectorOperation<&AtomString>,
1199 ) -> bool {
1200 match *ns {
1201 NamespaceConstraint::Specific(ns) => self
1202 .get_attr_enum(ns, local_name)
1203 .is_some_and(|value| value.eval_selector(operation)),
1204 NamespaceConstraint::Any => self
1205 .element
1206 .element
1207 .get_attr_vals_for_layout(local_name)
1208 .any(|v| v.eval_selector(operation)),
1209 }
1210 }
1211
1212 fn match_non_ts_pseudo_class(
1213 &self,
1214 _: &NonTSPseudoClass,
1215 _: &mut MatchingContext<Self::Impl>,
1216 ) -> bool {
1217 warn!("ServoThreadSafeLayoutElement::match_non_ts_pseudo_class called");
1219 false
1220 }
1221
1222 fn match_pseudo_element(
1223 &self,
1224 pseudo: &PseudoElement,
1225 context: &mut MatchingContext<Self::Impl>,
1226 ) -> bool {
1227 self.element.match_pseudo_element(pseudo, context)
1228 }
1229
1230 fn is_link(&self) -> bool {
1231 warn!("ServoThreadSafeLayoutElement::is_link called");
1232 false
1233 }
1234
1235 fn has_id(&self, _id: &AtomIdent, _case_sensitivity: CaseSensitivity) -> bool {
1236 debug!("ServoThreadSafeLayoutElement::has_id called");
1237 false
1238 }
1239
1240 #[inline]
1241 fn is_part(&self, _name: &AtomIdent) -> bool {
1242 debug!("ServoThreadSafeLayoutElement::is_part called");
1243 false
1244 }
1245
1246 fn imported_part(&self, _: &AtomIdent) -> Option<AtomIdent> {
1247 debug!("ServoThreadSafeLayoutElement::imported_part called");
1248 None
1249 }
1250
1251 fn has_class(&self, _name: &AtomIdent, _case_sensitivity: CaseSensitivity) -> bool {
1252 debug!("ServoThreadSafeLayoutElement::has_class called");
1253 false
1254 }
1255
1256 fn is_empty(&self) -> bool {
1257 warn!("ServoThreadSafeLayoutElement::is_empty called");
1258 false
1259 }
1260
1261 fn is_root(&self) -> bool {
1262 warn!("ServoThreadSafeLayoutElement::is_root called");
1263 false
1264 }
1265
1266 fn apply_selector_flags(&self, flags: ElementSelectorFlags) {
1267 let self_flags = flags.for_self();
1269 if !self_flags.is_empty() {
1270 self.element.element.insert_selector_flags(flags);
1271 }
1272
1273 let parent_flags = flags.for_parent();
1275 if !parent_flags.is_empty() {
1276 if let Some(p) = self.element.parent_element() {
1277 p.element.insert_selector_flags(flags);
1278 }
1279 }
1280 }
1281
1282 fn add_element_unique_hashes(&self, filter: &mut BloomFilter) -> bool {
1283 each_relevant_element_hash(self.element, |hash| {
1284 filter.insert_hash(hash & BLOOM_HASH_MASK)
1285 });
1286 true
1287 }
1288
1289 fn has_custom_state(&self, _name: &AtomIdent) -> bool {
1290 false
1291 }
1292}