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