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