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