1use apace_traits::node_id::NodeId;
5use std::ptr::NonNull;
6use std::sync::Mutex;
7use std::sync::atomic::Ordering;
8use stylo_takia::CopyStyleGetters;
9
10use crate::StyleThreading;
11use crate::layout::damage::compute_layout_damage;
12use crate::node::Node;
13use crate::node::NodeData;
14use markup5ever::{LocalName, LocalNameStaticSet, Namespace, NamespaceStaticSet, local_name, ns};
15use selectors::bloom::BLOOM_HASH_MASK;
16use selectors::{
17 Element, OpaqueElement,
18 attr::{AttrSelectorOperation, NamespaceConstraint},
19 matching::{ElementSelectorFlags, MatchingContext, VisitedHandlingMode},
20 sink::Push,
21};
22use style::CaseSensitivityExt;
23use style::animation::AnimationSetKey;
24use style::animation::AnimationState;
25use style::applicable_declarations::ApplicableDeclarationBlock;
26use style::bloom::each_relevant_element_hash;
27use style::color::AbsoluteColor;
28use style::data::{ElementDataMut, ElementDataRef};
29use style::global_style_data::STYLE_THREAD_POOL;
30
31static PARALLEL_TRAVERSAL_LOCK: Mutex<()> = Mutex::new(());
37use style::invalidation::element::restyle_hints::RestyleHint;
38use style::properties::ComputedValues;
39use style::properties::{Importance, PropertyDeclaration};
40use style::rule_tree::CascadeLevel;
41use style::rule_tree::CascadeOrigin;
42use style::selector_parser::PseudoElement;
43use style::selector_parser::RestyleDamage;
44use style::stylesheets::layer_rule::LayerOrder;
45use style::stylesheets::scope_rule::ImplicitScopeRoot;
46use style::values::AtomString;
47use style::values::specified::{NoCalcPercentage, XLang};
48use style::{
49 Atom,
50 context::{
51 QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters,
52 SharedStyleContext, StyleContext, ThreadLocalStyleContext,
53 },
54 dom::{LayoutIterator, NodeInfo, OpaqueNode, TDocument, TElement, TNode, TShadowRoot},
55 global_style_data::GLOBAL_STYLE_DATA,
56 properties::PropertyDeclarationBlock,
57 selector_parser::{NonTSPseudoClass, SelectorImpl},
58 servo_arc::{Arc, ArcBorrow},
59 shared_lock::{Locked, SharedRwLock, StylesheetGuards},
60 stylist::RuleInclusion,
61 thread_state::ThreadState,
62 traversal::{DomTraversal, resolve_style},
63 traversal_flags::TraversalFlags,
64 values::{AtomIdent, GenericAtomIdent},
65};
66use style_dom::ElementState;
67
68use style::values::computed::text::TextAlign as StyloTextAlign;
69
70impl crate::document::BaseDocument {
71 pub fn resolve_stylist(&mut self, now: f64) {
72 style::thread_state::enter(ThreadState::LAYOUT);
73
74 if self.stylist.stylesheets_have_changed() {
75 let state_only: Vec<_> = self
80 .snapshots
81 .iter()
82 .filter(|(_, snapshot)| snapshot.attrs.is_none())
83 .map(|(opaque, _)| NodeId::from_u64(opaque.id() as u64))
84 .collect();
85 for id in state_only {
86 self.snapshot_node(id);
87 }
88 }
89
90 let guard = &self.guard;
91 let guards = StylesheetGuards {
92 author: &guard.read(),
93 ua_or_user: &guard.read(),
94 };
95
96 let root = TDocument::as_node(&&self.nodes[self.root_node_id])
97 .first_element_child()
98 .unwrap()
99 .as_element()
100 .unwrap();
101
102 self.stylist
103 .flush(&guards)
104 .process_style(root, Some(&self.snapshots));
105
106 let mut sets = self.animations.sets.write();
108 for (key, set) in sets.iter_mut() {
109 let node_id = NodeId::from_u64(key.node.id() as u64);
110
111 let in_document = self
118 .nodes
119 .get(node_id)
120 .is_some_and(|node| node.flags.is_in_document());
121 if !in_document {
122 set.animations.clear();
123 set.transitions.clear();
124 continue;
125 }
126
127 self.nodes[node_id].set_restyle_hint(RestyleHint::RESTYLE_SELF);
128
129 for animation in set.animations.iter_mut() {
130 if animation.state == AnimationState::Pending && animation.started_at <= now {
131 animation.state = AnimationState::Running;
132 }
133 animation.iterate_if_necessary(now);
134
135 if animation.state == AnimationState::Running && animation.has_ended(now) {
136 animation.state = AnimationState::Finished;
137 }
138 }
139
140 for transition in set.transitions.iter_mut() {
141 if transition.state == AnimationState::Pending && transition.start_time <= now {
142 transition.state = AnimationState::Running;
143 }
144 if transition.state == AnimationState::Running && transition.has_ended(now) {
145 transition.state = AnimationState::Finished;
146 }
147 }
148 }
149 drop(sets);
150
151 let context = SharedStyleContext {
153 traversal_flags: TraversalFlags::empty(),
154 stylist: &self.stylist,
155 options: GLOBAL_STYLE_DATA.options.clone(),
156 guards,
157 visited_styles_enabled: false,
158 animations: self.animations.clone(),
159 current_time_for_animations: now,
160 snapshot_map: &self.snapshots,
161 registered_speculative_painters: &RegisteredPaintersImpl,
162 };
163
164 let root = self.root_element();
166 let token = RecalcStyle::pre_traverse(root, &context);
168
169 let mut nodes_needing_style_image_flush = Vec::new();
170 if token.should_traverse() {
171 let mut traverser = RecalcStyle::new(context);
173 let parallel = matches!(self.style_threading, StyleThreading::Parallel);
175 let _traversal_lock = parallel.then(|| {
176 PARALLEL_TRAVERSAL_LOCK
177 .lock()
178 .unwrap_or_else(|e| e.into_inner())
179 });
180 let pool_guard = parallel.then(|| STYLE_THREAD_POOL.pool());
181 let rayon_pool = pool_guard.as_ref().and_then(|g| g.as_ref());
182 style::driver::traverse_dom(&traverser, token, rayon_pool);
183 nodes_needing_style_image_flush =
184 std::mem::take(traverser.nodes_needing_style_image_flush.get_mut().unwrap());
185 }
186 self.pending_style_image_nodes
187 .extend(nodes_needing_style_image_flush);
188
189 for opaque in self.snapshots.keys() {
190 let id = NodeId::from_u64(opaque.id() as u64);
191 if let Some(node) = self.nodes.get_mut(id) {
192 node.set_has_snapshot(false);
193 }
194 }
195 self.snapshots.clear();
196
197 let mut sets = self.animations.sets.write();
198 for (key, set) in sets.iter_mut() {
199 let mut cursor = Some(NodeId::from_u64(key.node.id() as u64));
204 let mut hidden = false;
205 while let Some(id) = cursor {
206 let Some(node) = self.nodes.get(id) else {
207 hidden = true;
208 break;
209 };
210 if node
211 .primary_styles()
212 .is_some_and(|style| style.clone_display().is_none())
213 {
214 hidden = true;
215 break;
216 }
217 cursor = node.parent;
218 }
219 if hidden {
220 set.animations.clear();
221 set.transitions.clear();
222 continue;
223 }
224 set.clear_canceled_animations();
225 for animation in set.animations.iter_mut() {
226 animation.is_new = false;
227 }
228 for transition in set.transitions.iter_mut() {
229 transition.is_new = false;
230 }
231 }
232 let mut finished = Vec::new();
233 sets.retain(|key, state| {
234 let keep = !state.is_empty();
235 if !keep {
236 finished.push(key.node);
237 }
238 keep
239 });
240 for node in finished {
243 if sets.keys().any(|key| key.node == node) {
244 continue;
245 }
246 let node_id = NodeId::from_u64(node.id() as u64);
247 if let Some(data) = self.nodes.get(node_id).and_then(|node| node.element_data()) {
248 data.may_have_animations.store(false, Ordering::Relaxed);
249 }
250 }
251 self.has_active_animations = sets.values().any(|state| state.needs_animation_ticks());
252
253 self.stylist.rule_tree().maybe_gc();
255
256 style::thread_state::exit(ThreadState::LAYOUT);
257 }
258
259 pub fn resolve_undisplayed_style(&self, node_id: NodeId) -> Option<Arc<ComputedValues>> {
268 let node = self.nodes.get(node_id)?;
269 if !node.is_element() || !node.flags.is_in_document() {
270 return None;
271 }
272
273 style::thread_state::enter(ThreadState::LAYOUT);
274
275 let guard = &self.guard;
276 let guards = StylesheetGuards {
277 author: &guard.read(),
278 ua_or_user: &guard.read(),
279 };
280 let shared = SharedStyleContext {
281 traversal_flags: TraversalFlags::empty(),
282 stylist: &self.stylist,
283 options: GLOBAL_STYLE_DATA.options.clone(),
284 guards,
285 visited_styles_enabled: false,
286 animations: self.animations.clone(),
287 current_time_for_animations: 0.0,
288 snapshot_map: &self.snapshots,
289 registered_speculative_painters: &RegisteredPaintersImpl,
290 };
291 let styles = {
293 let mut thread_local = ThreadLocalStyleContext::new();
294 let mut context = StyleContext {
295 shared: &shared,
296 thread_local: &mut thread_local,
297 };
298 resolve_style(&mut context, node, RuleInclusion::All, None, None)
299 };
300
301 style::thread_state::exit(ThreadState::LAYOUT);
302
303 Some(styles.primary().clone())
304 }
305}
306
307type ApaceNode<'a> = &'a Node;
312
313impl<'a> TDocument for ApaceNode<'a> {
314 type ConcreteNode = ApaceNode<'a>;
315
316 fn as_node(&self) -> Self::ConcreteNode {
317 self
318 }
319
320 fn is_html_document(&self) -> bool {
321 true
322 }
323
324 fn quirks_mode(&self) -> QuirksMode {
325 QuirksMode::NoQuirks
326 }
327
328 fn shared_lock(&self) -> &SharedRwLock {
329 self.guard()
330 }
331}
332
333impl NodeInfo for ApaceNode<'_> {
334 fn is_element(&self) -> bool {
335 Node::is_element(self)
336 }
337
338 fn is_text_node(&self) -> bool {
339 Node::is_text_node(self)
340 }
341}
342
343impl<'a> TShadowRoot for ApaceNode<'a> {
344 type ConcreteNode = ApaceNode<'a>;
345
346 fn as_node(&self) -> Self::ConcreteNode {
347 self
348 }
349
350 fn host(&self) -> <Self::ConcreteNode as TNode>::ConcreteElement {
351 todo!("Shadow roots not implemented")
352 }
353
354 fn style_data<'b>(&self) -> Option<&'b style::stylist::CascadeData>
355 where
356 Self: 'b,
357 {
358 todo!("Shadow roots not implemented")
359 }
360}
361
362impl<'a> TNode for ApaceNode<'a> {
364 type ConcreteElement = ApaceNode<'a>;
365 type ConcreteDocument = ApaceNode<'a>;
366 type ConcreteShadowRoot = ApaceNode<'a>;
367
368 fn parent_node(&self) -> Option<Self> {
369 self.parent.map(|id| self.with(id))
370 }
371
372 fn first_child(&self) -> Option<Self> {
373 self.children.first().map(|id| self.with(*id))
374 }
375
376 fn last_child(&self) -> Option<Self> {
377 self.children.last().map(|id| self.with(*id))
378 }
379
380 fn prev_sibling(&self) -> Option<Self> {
381 self.backward(1)
382 }
383
384 fn next_sibling(&self) -> Option<Self> {
385 self.forward(1)
386 }
387
388 fn owner_doc(&self) -> Self::ConcreteDocument {
389 let mut node = *self;
391 while let Some(parent_id) = node.parent {
392 node = node.with(parent_id);
393 }
394 node
395 }
396
397 fn is_in_document(&self) -> bool {
398 true
399 }
400
401 fn traversal_parent(&self) -> Option<Self::ConcreteElement> {
406 self.parent_node().and_then(|node| node.as_element())
407 }
408
409 fn opaque(&self) -> OpaqueNode {
410 OpaqueNode(self.id.as_u64() as usize)
411 }
412
413 fn debug_id(self) -> usize {
414 self.id.as_u64() as usize
415 }
416
417 fn as_element(&self) -> Option<Self::ConcreteElement> {
418 match self.data {
419 NodeData::Element { .. } => Some(self),
420 _ => None,
421 }
422 }
423
424 fn as_document(&self) -> Option<Self::ConcreteDocument> {
425 match self.data {
426 NodeData::Document(_) => Some(self),
427 _ => None,
428 }
429 }
430
431 fn as_shadow_root(&self) -> Option<Self::ConcreteShadowRoot> {
432 None
434 }
435}
436
437impl selectors::Element for ApaceNode<'_> {
438 type Impl = SelectorImpl;
439
440 fn opaque(&self) -> selectors::OpaqueElement {
441 let non_null =
449 NonNull::new((self.id.as_u64() as usize).wrapping_add(1) as *mut ()).unwrap();
450 OpaqueElement::from_ptr(non_null)
451 }
452
453 fn parent_element(&self) -> Option<Self> {
454 TElement::traversal_parent(self)
455 }
456
457 fn parent_node_is_shadow_root(&self) -> bool {
458 false
459 }
460
461 fn containing_shadow_host(&self) -> Option<Self> {
462 None
463 }
464
465 fn is_pseudo_element(&self) -> bool {
466 matches!(self.data, NodeData::AnonymousBlock(_))
467 }
468
469 fn prev_sibling_element(&self) -> Option<Self> {
472 let mut n = 1;
473 while let Some(node) = self.backward(n) {
474 if node.is_element() {
475 return Some(node);
476 }
477 n += 1;
478 }
479
480 None
481 }
482
483 fn next_sibling_element(&self) -> Option<Self> {
484 let mut n = 1;
485 while let Some(node) = self.forward(n) {
486 if node.is_element() {
487 return Some(node);
488 }
489 n += 1;
490 }
491
492 None
493 }
494
495 fn first_element_child(&self) -> Option<Self> {
496 let mut children = self.dom_children();
497 children.find(|child| child.is_element())
498 }
499
500 fn is_html_element_in_html_document(&self) -> bool {
501 true }
503
504 fn has_local_name(&self, local_name: &LocalName) -> bool {
505 self.data.is_element_with_tag_name(local_name)
506 }
507
508 fn has_namespace(&self, ns: &Namespace) -> bool {
509 self.element_data().expect("Not an element").name.ns == *ns
510 }
511
512 fn is_same_type(&self, other: &Self) -> bool {
513 self.local_name() == other.local_name() && self.namespace() == other.namespace()
514 }
515
516 fn attr_matches(
517 &self,
518 ns: &NamespaceConstraint<&GenericAtomIdent<NamespaceStaticSet>>,
519 local_name: &GenericAtomIdent<LocalNameStaticSet>,
520 operation: &AttrSelectorOperation<&AtomString>,
521 ) -> bool {
522 let Some(attrs) = self.data.attrs() else {
523 return false;
524 };
525 attrs.iter().any(|attr| {
526 attr.name.local == local_name.0
527 && match ns {
528 NamespaceConstraint::Any => true,
529 NamespaceConstraint::Specific(ns) => attr.name.ns == ns.0,
530 }
531 && operation.eval_str(&attr.value)
532 })
533 }
534
535 fn match_non_ts_pseudo_class(
536 &self,
537 pseudo_class: &<Self::Impl as selectors::SelectorImpl>::NonTSPseudoClass,
538 _context: &mut MatchingContext<Self::Impl>,
539 ) -> bool {
540 match *pseudo_class {
541 NonTSPseudoClass::Active => self.element_state().contains(ElementState::ACTIVE),
542 NonTSPseudoClass::AnyLink => self
543 .element_state()
544 .intersects(ElementState::VISITED_OR_UNVISITED),
545 NonTSPseudoClass::Checked => self.element_state().contains(ElementState::CHECKED),
546 NonTSPseudoClass::Indeterminate => {
547 self.element_state().contains(ElementState::INDETERMINATE)
548 }
549 NonTSPseudoClass::Valid => false,
550 NonTSPseudoClass::Invalid => false,
551 NonTSPseudoClass::Defined => false,
552 NonTSPseudoClass::Disabled => self.element_state().contains(ElementState::DISABLED),
553 NonTSPseudoClass::Enabled => self.element_state().contains(ElementState::ENABLED),
554 NonTSPseudoClass::Focus => self.element_state().contains(ElementState::FOCUS),
555 NonTSPseudoClass::FocusWithin => {
556 self.element_state().contains(ElementState::FOCUS_WITHIN)
557 }
558 NonTSPseudoClass::FocusVisible => false,
559 NonTSPseudoClass::Fullscreen => false,
560 NonTSPseudoClass::Hover => self.element_state().contains(ElementState::HOVER),
561 NonTSPseudoClass::Lang(_) => false,
562 NonTSPseudoClass::CustomState(_) => false,
563 NonTSPseudoClass::Link => self.element_state().contains(ElementState::UNVISITED),
564 NonTSPseudoClass::PlaceholderShown => self
565 .element_state()
566 .contains(ElementState::PLACEHOLDER_SHOWN),
567 NonTSPseudoClass::ReadWrite => false,
568 NonTSPseudoClass::ReadOnly => false,
569 NonTSPseudoClass::ServoNonZeroBorder => false,
570 NonTSPseudoClass::Target => false,
571 NonTSPseudoClass::Visited => false,
572 NonTSPseudoClass::Autofill => false,
573 NonTSPseudoClass::Default => false,
574
575 NonTSPseudoClass::InRange => false,
576 NonTSPseudoClass::Modal => self.element_state().contains(ElementState::MODAL),
577 NonTSPseudoClass::MozInert => self.element_state().contains(ElementState::INERT),
578 NonTSPseudoClass::Open => self.element_state().contains(ElementState::OPEN),
579 NonTSPseudoClass::Optional => false,
580 NonTSPseudoClass::OutOfRange => false,
581 NonTSPseudoClass::PopoverOpen => {
582 self.element_state().contains(ElementState::POPOVER_OPEN)
583 }
584 NonTSPseudoClass::Required => false,
585 NonTSPseudoClass::UserInvalid => false,
586 NonTSPseudoClass::UserValid => false,
587 NonTSPseudoClass::MozMeterOptimum => false,
588 NonTSPseudoClass::MozMeterSubOptimum => false,
589 NonTSPseudoClass::MozMeterSubSubOptimum => false,
590 }
591 }
592
593 fn match_pseudo_element(
594 &self,
595 pe: &PseudoElement,
596 _context: &mut MatchingContext<Self::Impl>,
597 ) -> bool {
598 let pseudo = match self.try_stylo_element_data().and_then(|s| s.get()) {
599 Some(el) => el
600 .styles
601 .get_primary()
602 .and_then(|s| s.pseudo())
603 .or(match &self.data {
604 NodeData::AnonymousBlock(_) => Some(PseudoElement::ServoAnonymousBox),
605 _ => None,
606 }),
607 None => None,
608 };
609
610 pseudo.is_some_and(|psuedo| psuedo == *pe)
611 }
612
613 fn apply_selector_flags(&self, flags: ElementSelectorFlags) {
614 let self_flags = flags.for_self();
616 if !self_flags.is_empty() {
617 self.selector_flags()
618 .set(self.selector_flags().get() | self_flags);
619 }
620
621 let parent_flags = flags.for_parent();
623 if !parent_flags.is_empty() {
624 if let Some(parent) = self.parent_node() {
625 parent
626 .selector_flags()
627 .set(parent.selector_flags().get() | parent_flags);
628 }
629 }
630 }
631
632 fn is_link(&self) -> bool {
633 self.data.is_element_with_tag_name(&local_name!("a"))
634 }
635
636 fn is_html_slot_element(&self) -> bool {
637 false
638 }
639
640 fn has_id(
641 &self,
642 id: &<Self::Impl as selectors::SelectorImpl>::Identifier,
643 case_sensitivity: selectors::attr::CaseSensitivity,
644 ) -> bool {
645 self.element_data()
646 .and_then(|data| data.id.as_ref())
647 .map(|id_attr| case_sensitivity.eq_atom(id_attr, id))
648 .unwrap_or(false)
649 }
650
651 fn has_class(
652 &self,
653 search_name: &<Self::Impl as selectors::SelectorImpl>::Identifier,
654 case_sensitivity: selectors::attr::CaseSensitivity,
655 ) -> bool {
656 self.element_data().is_some_and(|data| {
657 data.classes
658 .iter()
659 .any(|class| case_sensitivity.eq_atom(class, search_name))
660 })
661 }
662
663 fn imported_part(
664 &self,
665 _name: &<Self::Impl as selectors::SelectorImpl>::Identifier,
666 ) -> Option<<Self::Impl as selectors::SelectorImpl>::Identifier> {
667 None
668 }
669
670 fn is_part(&self, _name: &<Self::Impl as selectors::SelectorImpl>::Identifier) -> bool {
671 false
672 }
673
674 fn is_empty(&self) -> bool {
675 self.dom_children().next().is_none()
676 }
677
678 fn is_root(&self) -> bool {
679 self.parent_node()
680 .and_then(|parent| parent.parent_node())
681 .is_none()
682 }
683
684 fn has_custom_state(
685 &self,
686 _name: &<Self::Impl as selectors::SelectorImpl>::Identifier,
687 ) -> bool {
688 false
689 }
690
691 fn add_element_unique_hashes(&self, filter: &mut selectors::bloom::BloomFilter) -> bool {
692 each_relevant_element_hash(*self, |hash| filter.insert_hash(hash & BLOOM_HASH_MASK));
693 true
694 }
695}
696
697impl<'a> TElement for ApaceNode<'a> {
698 type ConcreteNode = ApaceNode<'a>;
699
700 type TraversalChildrenIterator = Traverser<'a>;
701
702 fn as_node(&self) -> Self::ConcreteNode {
703 self
704 }
705
706 fn implicit_scope_for_sheet_in_shadow_root(
707 _opaque_host: OpaqueElement,
708 _sheet_index: usize,
709 ) -> Option<ImplicitScopeRoot> {
710 todo!();
715 }
716
717 fn traversal_children(&self) -> style::dom::LayoutIterator<Self::TraversalChildrenIterator> {
718 LayoutIterator(Traverser {
719 parent: self,
721 child_index: 0,
722 })
723 }
724
725 fn is_html_element(&self) -> bool {
726 self.is_element()
727 }
728
729 fn is_mathml_element(&self) -> bool {
731 false
732 }
733
734 fn is_svg_element(&self) -> bool {
736 false
737 }
738
739 fn style_attribute(&self) -> Option<ArcBorrow<'_, Locked<PropertyDeclarationBlock>>> {
740 self.element_data()
741 .expect("Not an element")
742 .style_attribute
743 .as_ref()
744 .map(|f| f.borrow_arc())
745 }
746
747 fn state(&self) -> ElementState {
748 *self.element_state()
749 }
750
751 fn has_part_attr(&self) -> bool {
752 false
753 }
754
755 fn exports_any_part(&self) -> bool {
756 false
757 }
758
759 fn id(&self) -> Option<&style::Atom> {
760 self.element_data().and_then(|data| data.id.as_ref())
761 }
762
763 fn each_class<F>(&self, mut callback: F)
764 where
765 F: FnMut(&style::values::AtomIdent),
766 {
767 if let Some(data) = self.element_data() {
768 for class in &data.classes {
769 callback(AtomIdent::cast(class));
770 }
771 }
772 }
773
774 fn each_attr_name<F>(&self, mut callback: F)
775 where
776 F: FnMut(&style::LocalName),
777 {
778 if let Some(attrs) = self.data.attrs() {
779 for attr in attrs.iter() {
780 callback(GenericAtomIdent::cast(&attr.name.local));
781 }
782 }
783 }
784
785 fn has_dirty_descendants(&self) -> bool {
786 Node::has_dirty_descendants(self)
787 }
788
789 fn has_snapshot(&self) -> bool {
790 Node::has_snapshot(self)
791 }
792
793 fn handled_snapshot(&self) -> bool {
794 self.snapshot_handled().load(Ordering::SeqCst)
795 }
796
797 unsafe fn set_handled_snapshot(&self) {
798 self.snapshot_handled().store(true, Ordering::SeqCst);
799 }
800
801 unsafe fn set_dirty_descendants(&self) {
808 Node::set_dirty_descendants(self);
809 }
810
811 unsafe fn unset_dirty_descendants(&self) {
812 Node::unset_dirty_descendants(self);
813 }
814
815 fn store_children_to_process(&self, _n: isize) {
816 unimplemented!()
817 }
818
819 fn did_process_child(&self) -> isize {
820 unimplemented!()
821 }
822
823 unsafe fn ensure_data(&self) -> ElementDataMut<'_> {
824 unsafe { self.stylo_element_data().ensure_init() }
826 }
827
828 unsafe fn clear_data(&self) {
829 unsafe { self.stylo_element_data().clear() }
831 }
832
833 fn has_data(&self) -> bool {
834 self.try_stylo_element_data().is_some_and(|s| s.has_data())
835 }
836
837 fn borrow_data(&self) -> Option<ElementDataRef<'_>> {
838 self.try_stylo_element_data()
842 .and_then(|s| s.get())
843 .filter(|data| data.styles.primary.is_some())
844 }
845
846 fn mutate_data(&self) -> Option<ElementDataMut<'_>> {
847 unsafe { self.stylo_element_data().unsafe_stylo_only_mut() }
848 }
849
850 fn skip_item_display_fixup(&self) -> bool {
851 false
852 }
853
854 fn may_have_animations(&self) -> bool {
855 self.element_data()
856 .is_some_and(|data| data.may_have_animations.load(Ordering::Relaxed))
857 }
858
859 fn set_may_have_animations(&self) {
860 if let Some(data) = self.element_data() {
861 data.may_have_animations.store(true, Ordering::Relaxed);
862 }
863 }
864
865 fn has_animations(&self, context: &SharedStyleContext) -> bool {
866 if !self.may_have_animations() {
867 return false;
868 }
869 let key = AnimationSetKey::new_for_non_pseudo(TNode::opaque(&TElement::as_node(self)));
870 context
871 .animations
872 .has_active_animations_or_transitions(&key)
873 }
874
875 fn has_css_animations(
876 &self,
877 context: &SharedStyleContext,
878 pseudo_element: Option<PseudoElement>,
879 ) -> bool {
880 if !self.may_have_animations() {
881 return false;
882 }
883 let key = AnimationSetKey::new(TNode::opaque(&TElement::as_node(self)), pseudo_element);
884 context.animations.has_active_animations(&key)
885 }
886
887 fn has_css_transitions(
888 &self,
889 context: &SharedStyleContext,
890 pseudo_element: Option<PseudoElement>,
891 ) -> bool {
892 if !self.may_have_animations() {
893 return false;
894 }
895 let key = AnimationSetKey::new(TNode::opaque(&TElement::as_node(self)), pseudo_element);
896 context.animations.has_active_transitions(&key)
897 }
898
899 fn animation_rule(
900 &self,
901 context: &SharedStyleContext,
902 ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
903 let opaque = TNode::opaque(&TElement::as_node(self));
904 context.animations.get_animation_declarations(
905 &AnimationSetKey::new_for_non_pseudo(opaque),
906 context.current_time_for_animations,
907 self.guard(),
908 )
909 }
910
911 fn transition_rule(
912 &self,
913 context: &SharedStyleContext,
914 ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
915 let opaque = TNode::opaque(&TElement::as_node(self));
916 context.animations.get_transition_declarations(
917 &AnimationSetKey::new_for_non_pseudo(opaque),
918 context.current_time_for_animations,
919 self.guard(),
920 )
921 }
922
923 fn shadow_root(&self) -> Option<<Self::ConcreteNode as TNode>::ConcreteShadowRoot> {
924 None
925 }
926
927 fn containing_shadow(&self) -> Option<<Self::ConcreteNode as TNode>::ConcreteShadowRoot> {
928 None
929 }
930
931 fn get_attr(&self, attr: &style::LocalName, ns: &style::Namespace) -> Option<String> {
932 self.attrs()?
934 .iter()
935 .find(|a| a.name.local == attr.0 && a.name.ns == ns.0)
936 .map(|a| a.value.clone())
937 }
938
939 fn lang_attr(&self) -> Option<style::selector_parser::AttrValue> {
940 None
941 }
942
943 fn match_element_lang(
944 &self,
945 _override_lang: Option<Option<style::selector_parser::AttrValue>>,
946 _value: &style::selector_parser::Lang,
947 ) -> bool {
948 false
949 }
950
951 fn is_html_document_body_element(&self) -> bool {
952 let is_body_element = self.data.is_element_with_tag_name(&local_name!("body"));
954
955 if !is_body_element {
957 return false;
958 }
959
960 let root_node = TNode::owner_doc(self);
962 let root_element = TDocument::as_node(&root_node)
963 .first_element_child()
964 .unwrap();
965 root_element.children.contains(&self.id)
966 }
967
968 fn synthesize_presentational_hints_for_legacy_attributes<V>(
969 &self,
970 _visited_handling: VisitedHandlingMode,
971 hints: &mut V,
972 ) where
973 V: Push<style::applicable_declarations::ApplicableDeclarationBlock>,
974 {
975 let Some(elem) = self.data.downcast_element() else {
976 return;
977 };
978
979 let tag = &elem.name.local;
980
981 let mut push_style = |decl: PropertyDeclaration| {
982 hints.push(ApplicableDeclarationBlock::from_declarations(
983 Arc::new(
984 self.guard()
985 .wrap(PropertyDeclarationBlock::with_one(decl, Importance::Normal)),
986 ),
987 CascadeLevel::new(CascadeOrigin::PresHints),
988 LayerOrder::root(),
989 ));
990 };
991
992 fn parse_color_attr(value: &str) -> Option<(u8, u8, u8, f32)> {
993 if !value.starts_with('#') {
994 return None;
995 }
996
997 let value = &value[1..];
998 if value.len() == 3 {
999 let r = u8::from_str_radix(&value[0..1], 16).ok()?;
1000 let g = u8::from_str_radix(&value[1..2], 16).ok()?;
1001 let b = u8::from_str_radix(&value[2..3], 16).ok()?;
1002 return Some((r, g, b, 1.0));
1003 }
1004
1005 if value.len() == 6 {
1006 let r = u8::from_str_radix(&value[0..2], 16).ok()?;
1007 let g = u8::from_str_radix(&value[2..4], 16).ok()?;
1008 let b = u8::from_str_radix(&value[4..6], 16).ok()?;
1009 return Some((r, g, b, 1.0));
1010 }
1011
1012 None
1013 }
1014
1015 fn parse_size_attr(
1023 value: &str,
1024 ignoring_zero: bool,
1025 ) -> Option<style::values::specified::LengthPercentage> {
1026 use style::servo::attr::{
1027 LengthOrPercentageOrAuto, parse_length, parse_nonzero_length,
1028 };
1029 use style::values::specified::{LengthPercentage, NoCalcLength};
1030 let parsed = if ignoring_zero {
1031 parse_nonzero_length(value)
1032 } else {
1033 parse_length(value)
1034 };
1035 match parsed {
1036 LengthOrPercentageOrAuto::Length(length) => Some(LengthPercentage::Length(
1037 NoCalcLength::from_px(length.to_f32_px()),
1038 )),
1039 LengthOrPercentageOrAuto::Percentage(fraction) => Some(
1040 LengthPercentage::Percentage(NoCalcPercentage::new(fraction)),
1041 ),
1042 LengthOrPercentageOrAuto::Auto => None,
1043 }
1044 }
1045
1046 fn parse_svg_size_attr(value: &str) -> Option<style::values::specified::LengthPercentage> {
1051 use style::values::specified::{LengthPercentage, NoCalcLength};
1052 use style_traits::ParsingMode;
1053
1054 let value = value.trim();
1055 if let Some(number) = value.strip_suffix('%') {
1056 let val: f32 = number.trim().parse().ok()?;
1057 return (val >= 0.0)
1058 .then(|| LengthPercentage::Percentage(NoCalcPercentage::new(val / 100.0)));
1059 }
1060
1061 let number_len = value
1065 .trim_end_matches(|c: char| c.is_ascii_alphabetic())
1066 .len();
1067 let (number, unit) = value.split_at(number_len);
1068 let val: f32 = number.trim().parse().ok().filter(|v| *v >= 0.0)?;
1069 let length = if unit.is_empty() {
1070 NoCalcLength::from_px(val)
1071 } else {
1072 NoCalcLength::parse_dimension_with_flags(ParsingMode::DEFAULT, false, val, unit)
1073 .ok()?
1074 };
1075 Some(LengthPercentage::Length(length))
1076 }
1077
1078 let is_image_input = *tag == local_name!("input")
1083 && elem.attrs().iter().any(|attr| {
1084 attr.name.local == local_name!("type") && attr.value.eq_ignore_ascii_case("image")
1085 });
1086
1087 let accepts_lang = elem.name.ns == ns!(html) || elem.name.ns == ns!(svg);
1089 let mut lang = None;
1090
1091 for attr in elem.attrs() {
1092 let name = &attr.name.local;
1093 let value = attr.value.as_str();
1094
1095 if *name == local_name!("lang") {
1096 if attr.name.ns == ns!(xml) {
1097 lang = Some(value);
1098 } else if attr.name.ns == ns!() && accepts_lang {
1099 lang = lang.or(Some(value));
1100 }
1101 }
1102
1103 if *name == local_name!("align") {
1104 use style::values::specified::TextAlign;
1105 let keyword = match value {
1106 "left" => Some(StyloTextAlign::MozLeft),
1107 "right" => Some(StyloTextAlign::MozRight),
1108 "center" => Some(StyloTextAlign::MozCenter),
1109 _ => None,
1110 };
1111
1112 if let Some(keyword) = keyword {
1113 push_style(PropertyDeclaration::TextAlign(TextAlign::Keyword(keyword)));
1114 }
1115 }
1116
1117 let is_width = *name == local_name!("width");
1122 let is_height = *name == local_name!("height");
1123 let is_embedded = *tag == local_name!("iframe")
1127 || *tag == local_name!("embed")
1128 || *tag == local_name!("video")
1129 || *tag == local_name!("object")
1130 || *tag == local_name!("img")
1131 || *tag == local_name!("marquee")
1132 || is_image_input;
1133 let maps_to_dimension = if is_width {
1134 is_embedded
1135 || *tag == local_name!("table")
1136 || *tag == local_name!("col")
1137 || *tag == local_name!("colgroup")
1138 || *tag == local_name!("tr")
1139 || *tag == local_name!("td")
1140 || *tag == local_name!("th")
1141 || *tag == local_name!("hr")
1142 } else if is_height {
1143 is_embedded
1144 || *tag == local_name!("table")
1145 || *tag == local_name!("thead")
1146 || *tag == local_name!("tbody")
1147 || *tag == local_name!("tfoot")
1148 || *tag == local_name!("tr")
1149 || *tag == local_name!("td")
1150 || *tag == local_name!("th")
1151 } else {
1152 false
1153 };
1154 if maps_to_dimension {
1155 let is_cell = *tag == local_name!("td") || *tag == local_name!("th");
1159 let ignoring_zero = is_cell || (is_width && *tag == local_name!("table"));
1160 if let Some(size) = parse_size_attr(value, ignoring_zero) {
1161 use style::values::generics::{NonNegative, length::Size};
1162 let size = Size::LengthPercentage(NonNegative(size));
1163 push_style(if is_width {
1164 PropertyDeclaration::Width(size)
1165 } else {
1166 PropertyDeclaration::Height(size)
1167 });
1168 }
1169 }
1170
1171 let takes_spacing = *tag == local_name!("embed")
1177 || *tag == local_name!("img")
1178 || *tag == local_name!("object")
1179 || *tag == local_name!("marquee")
1180 || is_image_input;
1181 if takes_spacing {
1182 let is_hspace = *name == local_name!("hspace");
1183 let is_vspace = *name == local_name!("vspace");
1184 if is_hspace || is_vspace {
1185 if let Some(size) = parse_size_attr(value, false) {
1186 use style::values::generics::length::GenericMargin;
1187 let margin = GenericMargin::LengthPercentage(size);
1188 if is_hspace {
1189 push_style(PropertyDeclaration::MarginLeft(margin.clone()));
1190 push_style(PropertyDeclaration::MarginRight(margin));
1191 } else {
1192 push_style(PropertyDeclaration::MarginTop(margin.clone()));
1193 push_style(PropertyDeclaration::MarginBottom(margin));
1194 }
1195 }
1196 }
1197 }
1198
1199 if *tag == local_name!("svg")
1205 && (*name == local_name!("width") || *name == local_name!("height"))
1206 {
1207 if let Some(size) = parse_svg_size_attr(value) {
1208 use style::values::generics::{NonNegative, length::Size};
1209 let size = Size::LengthPercentage(NonNegative(size));
1210 push_style(if *name == local_name!("width") {
1211 PropertyDeclaration::Width(size)
1212 } else {
1213 PropertyDeclaration::Height(size)
1214 });
1215 }
1216 }
1217
1218 if *name == local_name!("border")
1225 && (*tag == local_name!("img") || *tag == local_name!("object") || is_image_input)
1226 {
1227 if let Ok(px) = style::servo::attr::parse_unsigned_integer(value.chars()) {
1228 use style::values::specified::{BorderSideWidth, BorderStyle};
1229 let width = BorderSideWidth::from_px(px as f32);
1230 push_style(PropertyDeclaration::BorderTopWidth(width.clone()));
1231 push_style(PropertyDeclaration::BorderRightWidth(width.clone()));
1232 push_style(PropertyDeclaration::BorderBottomWidth(width.clone()));
1233 push_style(PropertyDeclaration::BorderLeftWidth(width));
1234 push_style(PropertyDeclaration::BorderTopStyle(BorderStyle::Solid));
1235 push_style(PropertyDeclaration::BorderRightStyle(BorderStyle::Solid));
1236 push_style(PropertyDeclaration::BorderBottomStyle(BorderStyle::Solid));
1237 push_style(PropertyDeclaration::BorderLeftStyle(BorderStyle::Solid));
1238 }
1239 }
1240
1241 if *tag == local_name!("body") {
1251 let sides: &[u8] = match &**name {
1254 "marginwidth" => b"lr",
1255 "marginheight" => b"tb",
1256 "leftmargin" => b"l",
1257 "topmargin" => b"t",
1258 _ => b"",
1259 };
1260 if !sides.is_empty() {
1261 if let Ok(px) = style::servo::attr::parse_unsigned_integer(value.chars()) {
1262 use style::values::generics::length::GenericMargin;
1263 use style::values::specified::{LengthPercentage, NoCalcLength};
1264 let margin = GenericMargin::LengthPercentage(LengthPercentage::Length(
1265 NoCalcLength::from_px(px as f32),
1266 ));
1267 for side in sides {
1268 push_style(match side {
1269 b'l' => PropertyDeclaration::MarginLeft(margin.clone()),
1270 b'r' => PropertyDeclaration::MarginRight(margin.clone()),
1271 b't' => PropertyDeclaration::MarginTop(margin.clone()),
1272 b'b' => PropertyDeclaration::MarginBottom(margin.clone()),
1273 _ => unreachable!("side table above only yields lrtb"),
1274 });
1275 }
1276 }
1277 }
1278 }
1279
1280 if *name == local_name!("bgcolor") {
1281 use style::values::specified::Color;
1282 if let Some((r, g, b, a)) = parse_color_attr(value) {
1283 push_style(PropertyDeclaration::BackgroundColor(
1284 Color::from_absolute_color(AbsoluteColor::srgb_legacy(r, g, b, a)),
1285 ));
1286 }
1287 }
1288
1289 if *name == local_name!("hidden") {
1290 use style::values::specified::Display;
1291 push_style(PropertyDeclaration::Display(Display::None));
1292 }
1293 }
1294
1295 if let Some(lang) = lang {
1296 push_style(PropertyDeclaration::XLang(XLang(Atom::from(lang))));
1297 }
1298 }
1299
1300 fn local_name(&self) -> &LocalName {
1301 &self.element_data().expect("Not an element").name.local
1302 }
1303
1304 fn namespace(&self) -> &Namespace {
1305 &self.element_data().expect("Not an element").name.ns
1306 }
1307
1308 fn query_container_size(
1309 &self,
1310 _display: &style::values::specified::Display,
1311 ) -> euclid::default::Size2D<Option<app_units::Au>> {
1312 Default::default()
1314 }
1315
1316 fn each_custom_state<F>(&self, _callback: F)
1317 where
1318 F: FnMut(&AtomIdent),
1319 {
1320 todo!()
1321 }
1322
1323 fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool {
1324 self.selector_flags().get().contains(flags)
1325 }
1326
1327 fn relative_selector_search_direction(&self) -> ElementSelectorFlags {
1328 let flags = self.selector_flags().get();
1329 if flags.contains(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING)
1330 {
1331 ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING
1332 } else if flags.contains(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR)
1333 {
1334 ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR
1335 } else if flags.contains(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING) {
1336 ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING
1337 } else {
1338 ElementSelectorFlags::empty()
1339 }
1340 }
1341
1342 fn compute_layout_damage(old: &ComputedValues, new: &ComputedValues) -> RestyleDamage {
1343 compute_layout_damage(old, new)
1344 }
1346
1347 }
1367
1368pub struct Traverser<'a> {
1369 parent: ApaceNode<'a>,
1371 child_index: usize,
1372}
1373
1374impl<'a> Iterator for Traverser<'a> {
1375 type Item = ApaceNode<'a>;
1376
1377 fn next(&mut self) -> Option<Self::Item> {
1378 let node_id = self.parent.children.get(self.child_index)?;
1379 let node = self.parent.with(*node_id);
1380
1381 self.child_index += 1;
1382
1383 Some(node)
1384 }
1385}
1386
1387impl std::hash::Hash for ApaceNode<'_> {
1388 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1389 state.write_u64(self.id.as_u64())
1390 }
1391}
1392
1393pub struct RegisteredPaintersImpl;
1397impl RegisteredSpeculativePainters for RegisteredPaintersImpl {
1398 fn get(&self, _name: &Atom) -> Option<&dyn RegisteredSpeculativePainter> {
1399 None
1400 }
1401}
1402
1403use style::traversal::recalc_style_at;
1404
1405pub struct RecalcStyle<'a> {
1406 context: SharedStyleContext<'a>,
1407 nodes_needing_style_image_flush: Mutex<Vec<NodeId>>,
1411}
1412
1413impl<'a> RecalcStyle<'a> {
1414 pub fn new(context: SharedStyleContext<'a>) -> Self {
1415 RecalcStyle {
1416 context,
1417 nodes_needing_style_image_flush: Mutex::new(Vec::new()),
1418 }
1419 }
1420}
1421
1422#[allow(unsafe_code)]
1423impl<'dom> DomTraversal<ApaceNode<'dom>> for RecalcStyle<'_> {
1424 fn process_preorder<F: FnMut(ApaceNode<'dom>)>(
1425 &self,
1426 context: &mut StyleContext<ApaceNode<'dom>>,
1427 node: ApaceNode<'dom>,
1428 note_child: F,
1429 ) {
1430 if let Some(el) = node.as_element() {
1431 let mut data = unsafe { el.ensure_data() };
1433 recalc_style_at(self, context, el, &mut data, note_child);
1434
1435 sync_pseudo_element_styles(el, &data, &self.nodes_needing_style_image_flush);
1436 if let Some(style) = data.styles.get_primary() {
1437 el.record_out_of_flow(style);
1438 }
1439
1440 if !data.damage.is_empty() {
1441 el.mark_damaged();
1444
1445 if data
1446 .styles
1447 .get_primary()
1448 .is_some_and(|style| needs_style_image_flush(el, style))
1449 {
1450 self.nodes_needing_style_image_flush
1451 .lock()
1452 .unwrap()
1453 .push(el.id);
1454 }
1455 }
1456
1457 el.unset_dirty_descendants();
1459 }
1460 }
1461
1462 #[inline]
1463 fn needs_postorder_traversal() -> bool {
1464 false
1465 }
1466
1467 fn process_postorder(
1468 &self,
1469 _style_context: &mut StyleContext<ApaceNode<'dom>>,
1470 _node: ApaceNode<'dom>,
1471 ) {
1472 panic!("this should never be called")
1473 }
1474
1475 #[inline]
1476 fn shared_context(&self) -> &SharedStyleContext<'_> {
1477 &self.context
1478 }
1479}
1480
1481#[allow(unsafe_code)]
1503fn sync_pseudo_element_styles(
1504 el: &Node,
1505 data: &style::data::ElementData,
1506 nodes_needing_style_image_flush: &Mutex<Vec<NodeId>>,
1507) {
1508 let before_node_id = el.before();
1509 let after_node_id = el.after();
1510 if before_node_id.is_none() && after_node_id.is_none() {
1511 return;
1512 }
1513
1514 let pseudos = data.styles.pseudos.as_array();
1516 let before_style = pseudos[1].clone();
1517 let after_style = pseudos[0].clone();
1518
1519 for (pe_node_id, pe_style) in [(before_node_id, before_style), (after_node_id, after_style)] {
1523 let (Some(pe_node_id), Some(pe_style)) = (pe_node_id, pe_style) else {
1524 continue;
1525 };
1526 let pe_node = el.with(pe_node_id);
1527 let Some(stylo_data) = pe_node.try_stylo_element_data() else {
1528 continue;
1529 };
1530 let mut pe_data = match unsafe { stylo_data.unsafe_stylo_only_mut() } {
1531 Some(data) => data,
1532 None => continue,
1533 };
1534 let Some(old_style) = pe_data.styles.primary.clone() else {
1535 continue;
1536 };
1537 if std::ptr::eq(&*old_style, &*pe_style) {
1538 continue;
1539 }
1540
1541 let diff = RestyleDamage::compute_style_difference::<&Node>(&old_style, &pe_style);
1542 if !diff.damage.is_empty() {
1543 pe_data.damage.insert(diff.damage);
1544 pe_node.mark_damaged();
1545
1546 if needs_style_image_flush(pe_node, &pe_style) {
1547 nodes_needing_style_image_flush
1548 .lock()
1549 .unwrap()
1550 .push(pe_node_id);
1551 }
1552 }
1553 pe_node.record_out_of_flow(&pe_style);
1554 pe_data.styles.primary = Some(pe_style);
1555 pe_data.set_restyled();
1556 }
1557}
1558
1559fn needs_style_image_flush(node: &Node, style: &ComputedValues) -> bool {
1563 use crate::node::ImageResourceData;
1564 use style::url::ComputedUrl;
1565 use style::values::computed::image::Image;
1566
1567 fn out_of_sync(style_images: &[Image], stored: &[Option<ImageResourceData>]) -> bool {
1568 if style_images.len() != stored.len() {
1569 return style_images
1572 .iter()
1573 .any(|image| matches!(image, Image::Url(_)))
1574 || stored.iter().any(Option::is_some);
1575 }
1576 std::iter::zip(style_images, stored).any(|(style_image, stored)| {
1577 match (style_image, stored) {
1578 (Image::Url(ComputedUrl::Valid(url)), Some(data)) => **url != *data.url,
1579 (Image::Url(ComputedUrl::Valid(_)), None) => true,
1580 (_, Some(_)) => true,
1581 (_, None) => false,
1582 }
1583 })
1584 }
1585
1586 node.data.downcast_element().is_some_and(|el| {
1587 out_of_sync(
1588 &style.get_background().background_image.0,
1589 &el.background_images,
1590 ) || out_of_sync(&style.get_svg().mask_image.0, &el.mask_images)
1591 })
1592}
1593
1594#[test]
1595fn assert_size_of_equals() {
1596 }
1612
1613#[test]
1614fn parse_inline() {
1615 }