1use std::ptr::NonNull;
5use std::sync::atomic::Ordering;
6
7use crate::StyleThreading;
8use crate::layout::damage::compute_layout_damage;
9use crate::node::Node;
10use crate::node::NodeData;
11use markup5ever::{LocalName, LocalNameStaticSet, Namespace, NamespaceStaticSet, local_name};
12use selectors::bloom::BLOOM_HASH_MASK;
13use selectors::{
14 Element, OpaqueElement,
15 attr::{AttrSelectorOperation, NamespaceConstraint},
16 matching::{ElementSelectorFlags, MatchingContext, VisitedHandlingMode},
17 sink::Push,
18};
19use style::CaseSensitivityExt;
20use style::animation::AnimationSetKey;
21use style::animation::AnimationState;
22use style::applicable_declarations::ApplicableDeclarationBlock;
23use style::bloom::each_relevant_element_hash;
24use style::color::AbsoluteColor;
25use style::data::{ElementDataMut, ElementDataRef};
26use style::dom::AttributeProvider;
27use style::global_style_data::STYLE_THREAD_POOL;
28use style::invalidation::element::restyle_hints::RestyleHint;
29use style::properties::ComputedValues;
30use style::properties::{Importance, PropertyDeclaration};
31use style::rule_tree::CascadeLevel;
32use style::rule_tree::CascadeOrigin;
33use style::selector_parser::PseudoElement;
34use style::selector_parser::RestyleDamage;
35use style::stylesheets::layer_rule::LayerOrder;
36use style::stylesheets::scope_rule::ImplicitScopeRoot;
37use style::values::AtomString;
38use style::values::specified::NoCalcPercentage;
39use style::{
40 Atom,
41 context::{
42 QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters,
43 SharedStyleContext, StyleContext,
44 },
45 dom::{LayoutIterator, NodeInfo, OpaqueNode, TDocument, TElement, TNode, TShadowRoot},
46 global_style_data::GLOBAL_STYLE_DATA,
47 properties::PropertyDeclarationBlock,
48 selector_parser::{NonTSPseudoClass, SelectorImpl},
49 servo_arc::{Arc, ArcBorrow},
50 shared_lock::{Locked, SharedRwLock, StylesheetGuards},
51 thread_state::ThreadState,
52 traversal::{DomTraversal, PerLevelTraversalData},
53 traversal_flags::TraversalFlags,
54 values::{AtomIdent, GenericAtomIdent},
55};
56use style_dom::ElementState;
57
58use style::values::computed::text::TextAlign as StyloTextAlign;
59
60impl crate::document::BaseDocument {
61 pub fn resolve_stylist(&mut self, now: f64) {
62 style::thread_state::enter(ThreadState::LAYOUT);
63
64 let guard = &self.guard;
65 let guards = StylesheetGuards {
66 author: &guard.read(),
67 ua_or_user: &guard.read(),
68 };
69
70 let root = TDocument::as_node(&&self.nodes[0])
71 .first_element_child()
72 .unwrap()
73 .as_element()
74 .unwrap();
75
76 self.stylist
77 .flush(&guards)
78 .process_style(root, Some(&self.snapshots));
79
80 let mut sets = self.animations.sets.write();
82 for (key, set) in sets.iter_mut() {
83 let node_id = key.node.id();
84
85 let in_document = self
92 .nodes
93 .get(node_id)
94 .is_some_and(|node| node.flags.is_in_document());
95 if !in_document {
96 set.animations.clear();
97 set.transitions.clear();
98 continue;
99 }
100
101 self.nodes[node_id].set_restyle_hint(RestyleHint::RESTYLE_SELF);
102
103 for animation in set.animations.iter_mut() {
104 if animation.state == AnimationState::Pending && animation.started_at <= now {
105 animation.state = AnimationState::Running;
106 }
107 animation.iterate_if_necessary(now);
108
109 if animation.state == AnimationState::Running && animation.has_ended(now) {
110 animation.state = AnimationState::Finished;
111 }
112 }
113
114 for transition in set.transitions.iter_mut() {
115 if transition.state == AnimationState::Pending && transition.start_time <= now {
116 transition.state = AnimationState::Running;
117 }
118 if transition.state == AnimationState::Running && transition.has_ended(now) {
119 transition.state = AnimationState::Finished;
120 }
121 }
122 }
123 drop(sets);
124
125 let context = SharedStyleContext {
127 traversal_flags: TraversalFlags::empty(),
128 stylist: &self.stylist,
129 options: GLOBAL_STYLE_DATA.options.clone(),
130 guards,
131 visited_styles_enabled: false,
132 animations: self.animations.clone(),
133 current_time_for_animations: now,
134 snapshot_map: &self.snapshots,
135 registered_speculative_painters: &RegisteredPaintersImpl,
136 };
137
138 let root = self.root_element();
140 let token = RecalcStyle::pre_traverse(root, &context);
142
143 if token.should_traverse() {
144 let traverser = RecalcStyle::new(context);
146 let pool_guard = matches!(self.style_threading, StyleThreading::Parallel)
148 .then(|| STYLE_THREAD_POOL.pool());
149 let rayon_pool = pool_guard.as_ref().and_then(|g| g.as_ref());
150 style::driver::traverse_dom(&traverser, token, rayon_pool);
151 }
152
153 for opaque in self.snapshots.keys() {
154 let id = opaque.id();
155 if let Some(node) = self.nodes.get_mut(id) {
156 node.has_snapshot = false;
157 }
158 }
159 self.snapshots.clear();
160
161 let mut sets = self.animations.sets.write();
162 for set in sets.values_mut() {
163 set.clear_canceled_animations();
164 for animation in set.animations.iter_mut() {
165 animation.is_new = false;
166 }
167 for transition in set.transitions.iter_mut() {
168 transition.is_new = false;
169 }
170 }
171 sets.retain(|_, state| !state.is_empty());
172 self.has_active_animations = sets.values().any(|state| state.needs_animation_ticks());
173
174 self.stylist.rule_tree().maybe_gc();
176
177 style::thread_state::exit(ThreadState::LAYOUT);
178 }
179}
180
181type BlitzNode<'a> = &'a Node;
186
187impl<'a> TDocument for BlitzNode<'a> {
188 type ConcreteNode = BlitzNode<'a>;
189
190 fn as_node(&self) -> Self::ConcreteNode {
191 self
192 }
193
194 fn is_html_document(&self) -> bool {
195 true
196 }
197
198 fn quirks_mode(&self) -> QuirksMode {
199 QuirksMode::NoQuirks
200 }
201
202 fn shared_lock(&self) -> &SharedRwLock {
203 &self.guard
204 }
205}
206
207impl NodeInfo for BlitzNode<'_> {
208 fn is_element(&self) -> bool {
209 Node::is_element(self)
210 }
211
212 fn is_text_node(&self) -> bool {
213 Node::is_text_node(self)
214 }
215}
216
217impl<'a> TShadowRoot for BlitzNode<'a> {
218 type ConcreteNode = BlitzNode<'a>;
219
220 fn as_node(&self) -> Self::ConcreteNode {
221 self
222 }
223
224 fn host(&self) -> <Self::ConcreteNode as TNode>::ConcreteElement {
225 todo!("Shadow roots not implemented")
226 }
227
228 fn style_data<'b>(&self) -> Option<&'b style::stylist::CascadeData>
229 where
230 Self: 'b,
231 {
232 todo!("Shadow roots not implemented")
233 }
234}
235
236impl<'a> TNode for BlitzNode<'a> {
238 type ConcreteElement = BlitzNode<'a>;
239 type ConcreteDocument = BlitzNode<'a>;
240 type ConcreteShadowRoot = BlitzNode<'a>;
241
242 fn parent_node(&self) -> Option<Self> {
243 self.parent.map(|id| self.with(id))
244 }
245
246 fn first_child(&self) -> Option<Self> {
247 self.children.first().map(|id| self.with(*id))
248 }
249
250 fn last_child(&self) -> Option<Self> {
251 self.children.last().map(|id| self.with(*id))
252 }
253
254 fn prev_sibling(&self) -> Option<Self> {
255 self.backward(1)
256 }
257
258 fn next_sibling(&self) -> Option<Self> {
259 self.forward(1)
260 }
261
262 fn owner_doc(&self) -> Self::ConcreteDocument {
263 self.with(1)
264 }
265
266 fn is_in_document(&self) -> bool {
267 true
268 }
269
270 fn traversal_parent(&self) -> Option<Self::ConcreteElement> {
275 self.parent_node().and_then(|node| node.as_element())
276 }
277
278 fn opaque(&self) -> OpaqueNode {
279 OpaqueNode(self.id)
280 }
281
282 fn debug_id(self) -> usize {
283 self.id
284 }
285
286 fn as_element(&self) -> Option<Self::ConcreteElement> {
287 match self.data {
288 NodeData::Element { .. } => Some(self),
289 _ => None,
290 }
291 }
292
293 fn as_document(&self) -> Option<Self::ConcreteDocument> {
294 match self.data {
295 NodeData::Document => Some(self),
296 _ => None,
297 }
298 }
299
300 fn as_shadow_root(&self) -> Option<Self::ConcreteShadowRoot> {
301 None
303 }
304}
305
306impl AttributeProvider for BlitzNode<'_> {
307 fn get_attr(&self, attr: &style::LocalName, _ns: &style::Namespace) -> Option<String> {
308 self.attr(attr.0.clone()).map(|s| s.to_string())
310 }
311}
312
313impl selectors::Element for BlitzNode<'_> {
314 type Impl = SelectorImpl;
315
316 fn opaque(&self) -> selectors::OpaqueElement {
317 let non_null = NonNull::new((self.id + 1) as *mut ()).unwrap();
325 OpaqueElement::from_non_null_ptr(non_null)
326 }
327
328 fn parent_element(&self) -> Option<Self> {
329 TElement::traversal_parent(self)
330 }
331
332 fn parent_node_is_shadow_root(&self) -> bool {
333 false
334 }
335
336 fn containing_shadow_host(&self) -> Option<Self> {
337 None
338 }
339
340 fn is_pseudo_element(&self) -> bool {
341 matches!(self.data, NodeData::AnonymousBlock(_))
342 }
343
344 fn prev_sibling_element(&self) -> Option<Self> {
347 let mut n = 1;
348 while let Some(node) = self.backward(n) {
349 if node.is_element() {
350 return Some(node);
351 }
352 n += 1;
353 }
354
355 None
356 }
357
358 fn next_sibling_element(&self) -> Option<Self> {
359 let mut n = 1;
360 while let Some(node) = self.forward(n) {
361 if node.is_element() {
362 return Some(node);
363 }
364 n += 1;
365 }
366
367 None
368 }
369
370 fn first_element_child(&self) -> Option<Self> {
371 let mut children = self.dom_children();
372 children.find(|child| child.is_element())
373 }
374
375 fn is_html_element_in_html_document(&self) -> bool {
376 true }
378
379 fn has_local_name(&self, local_name: &LocalName) -> bool {
380 self.data.is_element_with_tag_name(local_name)
381 }
382
383 fn has_namespace(&self, ns: &Namespace) -> bool {
384 self.element_data().expect("Not an element").name.ns == *ns
385 }
386
387 fn is_same_type(&self, other: &Self) -> bool {
388 self.local_name() == other.local_name() && self.namespace() == other.namespace()
389 }
390
391 fn attr_matches(
392 &self,
393 _ns: &NamespaceConstraint<&GenericAtomIdent<NamespaceStaticSet>>,
394 local_name: &GenericAtomIdent<LocalNameStaticSet>,
395 operation: &AttrSelectorOperation<&AtomString>,
396 ) -> bool {
397 match self.data.attr(local_name.0.clone()) {
398 None => false,
399 Some(attr_value) => operation.eval_str(attr_value),
400 }
401 }
402
403 fn match_non_ts_pseudo_class(
404 &self,
405 pseudo_class: &<Self::Impl as selectors::SelectorImpl>::NonTSPseudoClass,
406 _context: &mut MatchingContext<Self::Impl>,
407 ) -> bool {
408 match *pseudo_class {
409 NonTSPseudoClass::Active => self.element_state.contains(ElementState::ACTIVE),
410 NonTSPseudoClass::AnyLink => self
411 .data
412 .downcast_element()
413 .map(|elem| {
414 (elem.name.local == local_name!("a") || elem.name.local == local_name!("area"))
415 && elem.attr(local_name!("href")).is_some()
416 })
417 .unwrap_or(false),
418 NonTSPseudoClass::Checked => self
419 .data
420 .downcast_element()
421 .and_then(|elem| elem.checkbox_input_checked())
422 .unwrap_or(false),
423 NonTSPseudoClass::Valid => false,
424 NonTSPseudoClass::Invalid => false,
425 NonTSPseudoClass::Defined => false,
426 NonTSPseudoClass::Disabled => self.element_state.contains(ElementState::DISABLED),
427 NonTSPseudoClass::Enabled => self.element_state.contains(ElementState::ENABLED),
428 NonTSPseudoClass::Focus => self.element_state.contains(ElementState::FOCUS),
429 NonTSPseudoClass::FocusWithin => false,
430 NonTSPseudoClass::FocusVisible => false,
431 NonTSPseudoClass::Fullscreen => false,
432 NonTSPseudoClass::Hover => self.element_state.contains(ElementState::HOVER),
433 NonTSPseudoClass::Indeterminate => false,
434 NonTSPseudoClass::Lang(_) => false,
435 NonTSPseudoClass::CustomState(_) => false,
436 NonTSPseudoClass::Link => self
437 .data
438 .downcast_element()
439 .map(|elem| {
440 (elem.name.local == local_name!("a") || elem.name.local == local_name!("area"))
441 && elem.attr(local_name!("href")).is_some()
442 })
443 .unwrap_or(false),
444 NonTSPseudoClass::PlaceholderShown => false,
445 NonTSPseudoClass::ReadWrite => false,
446 NonTSPseudoClass::ReadOnly => false,
447 NonTSPseudoClass::ServoNonZeroBorder => false,
448 NonTSPseudoClass::Target => false,
449 NonTSPseudoClass::Visited => false,
450 NonTSPseudoClass::Autofill => false,
451 NonTSPseudoClass::Default => false,
452
453 NonTSPseudoClass::InRange => false,
454 NonTSPseudoClass::Modal => false,
455 NonTSPseudoClass::Open => false,
456 NonTSPseudoClass::Optional => false,
457 NonTSPseudoClass::OutOfRange => false,
458 NonTSPseudoClass::PopoverOpen => false,
459 NonTSPseudoClass::Required => false,
460 NonTSPseudoClass::UserInvalid => false,
461 NonTSPseudoClass::UserValid => false,
462 NonTSPseudoClass::MozMeterOptimum => false,
463 NonTSPseudoClass::MozMeterSubOptimum => false,
464 NonTSPseudoClass::MozMeterSubSubOptimum => false,
465 }
466 }
467
468 fn match_pseudo_element(
469 &self,
470 pe: &PseudoElement,
471 _context: &mut MatchingContext<Self::Impl>,
472 ) -> bool {
473 let pseudo = match self.stylo_element_data.get() {
474 Some(el) => el.styles.primary().pseudo().or(match &self.data {
475 NodeData::AnonymousBlock(_) => Some(PseudoElement::ServoAnonymousBox),
476 _ => None,
477 }),
478 None => None,
479 };
480
481 pseudo.is_some_and(|psuedo| psuedo == *pe)
482 }
483
484 fn apply_selector_flags(&self, flags: ElementSelectorFlags) {
485 let self_flags = flags.for_self();
487 if !self_flags.is_empty() {
488 self.selector_flags
489 .set(self.selector_flags.get() | self_flags);
490 }
491
492 let parent_flags = flags.for_parent();
494 if !parent_flags.is_empty() {
495 if let Some(parent) = self.parent_node() {
496 parent
497 .selector_flags
498 .set(parent.selector_flags.get() | parent_flags);
499 }
500 }
501 }
502
503 fn is_link(&self) -> bool {
504 self.data.is_element_with_tag_name(&local_name!("a"))
505 }
506
507 fn is_html_slot_element(&self) -> bool {
508 false
509 }
510
511 fn has_id(
512 &self,
513 id: &<Self::Impl as selectors::SelectorImpl>::Identifier,
514 case_sensitivity: selectors::attr::CaseSensitivity,
515 ) -> bool {
516 self.element_data()
517 .and_then(|data| data.id.as_ref())
518 .map(|id_attr| case_sensitivity.eq_atom(id_attr, id))
519 .unwrap_or(false)
520 }
521
522 fn has_class(
523 &self,
524 search_name: &<Self::Impl as selectors::SelectorImpl>::Identifier,
525 case_sensitivity: selectors::attr::CaseSensitivity,
526 ) -> bool {
527 let class_attr = self.data.attr(local_name!("class"));
528 if let Some(class_attr) = class_attr {
529 for pheme in class_attr.split_ascii_whitespace() {
531 let atom = Atom::from(pheme);
532 if case_sensitivity.eq_atom(&atom, search_name) {
533 return true;
534 }
535 }
536 }
537
538 false
539 }
540
541 fn imported_part(
542 &self,
543 _name: &<Self::Impl as selectors::SelectorImpl>::Identifier,
544 ) -> Option<<Self::Impl as selectors::SelectorImpl>::Identifier> {
545 None
546 }
547
548 fn is_part(&self, _name: &<Self::Impl as selectors::SelectorImpl>::Identifier) -> bool {
549 false
550 }
551
552 fn is_empty(&self) -> bool {
553 self.dom_children().next().is_none()
554 }
555
556 fn is_root(&self) -> bool {
557 self.parent_node()
558 .and_then(|parent| parent.parent_node())
559 .is_none()
560 }
561
562 fn has_custom_state(
563 &self,
564 _name: &<Self::Impl as selectors::SelectorImpl>::Identifier,
565 ) -> bool {
566 false
567 }
568
569 fn add_element_unique_hashes(&self, filter: &mut selectors::bloom::BloomFilter) -> bool {
570 each_relevant_element_hash(*self, |hash| filter.insert_hash(hash & BLOOM_HASH_MASK));
571 true
572 }
573}
574
575impl<'a> TElement for BlitzNode<'a> {
576 type ConcreteNode = BlitzNode<'a>;
577
578 type TraversalChildrenIterator = Traverser<'a>;
579
580 fn as_node(&self) -> Self::ConcreteNode {
581 self
582 }
583
584 fn implicit_scope_for_sheet_in_shadow_root(
585 _opaque_host: OpaqueElement,
586 _sheet_index: usize,
587 ) -> Option<ImplicitScopeRoot> {
588 todo!();
593 }
594
595 fn traversal_children(&self) -> style::dom::LayoutIterator<Self::TraversalChildrenIterator> {
596 LayoutIterator(Traverser {
597 parent: self,
599 child_index: 0,
600 })
601 }
602
603 fn is_html_element(&self) -> bool {
604 self.is_element()
605 }
606
607 fn is_mathml_element(&self) -> bool {
609 false
610 }
611
612 fn is_svg_element(&self) -> bool {
614 false
615 }
616
617 fn style_attribute(&self) -> Option<ArcBorrow<'_, Locked<PropertyDeclarationBlock>>> {
618 self.element_data()
619 .expect("Not an element")
620 .style_attribute
621 .as_ref()
622 .map(|f| f.borrow_arc())
623 }
624
625 fn state(&self) -> ElementState {
626 self.element_state
627 }
628
629 fn has_part_attr(&self) -> bool {
630 false
631 }
632
633 fn exports_any_part(&self) -> bool {
634 false
635 }
636
637 fn id(&self) -> Option<&style::Atom> {
638 self.element_data().and_then(|data| data.id.as_ref())
639 }
640
641 fn each_class<F>(&self, mut callback: F)
642 where
643 F: FnMut(&style::values::AtomIdent),
644 {
645 let class_attr = self.data.attr(local_name!("class"));
646 if let Some(class_attr) = class_attr {
647 for pheme in class_attr.split_ascii_whitespace() {
649 let atom = Atom::from(pheme); callback(AtomIdent::cast(&atom));
651 }
652 }
653 }
654
655 fn each_attr_name<F>(&self, mut callback: F)
656 where
657 F: FnMut(&style::LocalName),
658 {
659 if let Some(attrs) = self.data.attrs() {
660 for attr in attrs.iter() {
661 callback(&GenericAtomIdent(attr.name.local.clone()));
662 }
663 }
664 }
665
666 fn has_dirty_descendants(&self) -> bool {
667 Node::has_dirty_descendants(self)
668 }
669
670 fn has_snapshot(&self) -> bool {
671 self.has_snapshot
672 }
673
674 fn handled_snapshot(&self) -> bool {
675 self.snapshot_handled.load(Ordering::SeqCst)
676 }
677
678 unsafe fn set_handled_snapshot(&self) {
679 self.snapshot_handled.store(true, Ordering::SeqCst);
680 }
681
682 unsafe fn set_dirty_descendants(&self) {
683 Node::set_dirty_descendants(self);
684 Node::mark_ancestors_dirty(self);
685 }
686
687 unsafe fn unset_dirty_descendants(&self) {
688 Node::unset_dirty_descendants(self);
689 }
690
691 fn store_children_to_process(&self, _n: isize) {
692 unimplemented!()
693 }
694
695 fn did_process_child(&self) -> isize {
696 unimplemented!()
697 }
698
699 unsafe fn ensure_data(&self) -> ElementDataMut<'_> {
700 unsafe { self.stylo_element_data.ensure_init() }
702 }
703
704 unsafe fn clear_data(&self) {
705 unsafe { self.stylo_element_data.clear() }
707 }
708
709 fn has_data(&self) -> bool {
710 self.stylo_element_data.has_data()
711 }
712
713 fn borrow_data(&self) -> Option<ElementDataRef<'_>> {
714 self.stylo_element_data.get()
715 }
716
717 fn mutate_data(&self) -> Option<ElementDataMut<'_>> {
718 unsafe { self.stylo_element_data.unsafe_stylo_only_mut() }
719 }
720
721 fn skip_item_display_fixup(&self) -> bool {
722 false
723 }
724
725 fn may_have_animations(&self) -> bool {
726 true
727 }
728
729 fn has_animations(&self, context: &SharedStyleContext) -> bool {
730 self.has_css_animations(context, None) || self.has_css_transitions(context, None)
731 }
732
733 fn has_css_animations(
734 &self,
735 context: &SharedStyleContext,
736 pseudo_element: Option<PseudoElement>,
737 ) -> bool {
738 let key = AnimationSetKey::new(TNode::opaque(&TElement::as_node(self)), pseudo_element);
739 context.animations.has_active_animations(&key)
740 }
741
742 fn has_css_transitions(
743 &self,
744 context: &SharedStyleContext,
745 pseudo_element: Option<PseudoElement>,
746 ) -> bool {
747 let key = AnimationSetKey::new(TNode::opaque(&TElement::as_node(self)), pseudo_element);
748 context.animations.has_active_transitions(&key)
749 }
750
751 fn animation_rule(
752 &self,
753 context: &SharedStyleContext,
754 ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
755 let opaque = TNode::opaque(&TElement::as_node(self));
756 context.animations.get_animation_declarations(
757 &AnimationSetKey::new_for_non_pseudo(opaque),
758 context.current_time_for_animations,
759 &self.guard,
760 )
761 }
762
763 fn transition_rule(
764 &self,
765 context: &SharedStyleContext,
766 ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
767 let opaque = TNode::opaque(&TElement::as_node(self));
768 context.animations.get_transition_declarations(
769 &AnimationSetKey::new_for_non_pseudo(opaque),
770 context.current_time_for_animations,
771 &self.guard,
772 )
773 }
774
775 fn shadow_root(&self) -> Option<<Self::ConcreteNode as TNode>::ConcreteShadowRoot> {
776 None
777 }
778
779 fn containing_shadow(&self) -> Option<<Self::ConcreteNode as TNode>::ConcreteShadowRoot> {
780 None
781 }
782
783 fn lang_attr(&self) -> Option<style::selector_parser::AttrValue> {
784 None
785 }
786
787 fn match_element_lang(
788 &self,
789 _override_lang: Option<Option<style::selector_parser::AttrValue>>,
790 _value: &style::selector_parser::Lang,
791 ) -> bool {
792 false
793 }
794
795 fn is_html_document_body_element(&self) -> bool {
796 let is_body_element = self.data.is_element_with_tag_name(&local_name!("body"));
798
799 if !is_body_element {
801 return false;
802 }
803
804 let root_node = &self.tree()[0];
806 let root_element = TDocument::as_node(&root_node)
807 .first_element_child()
808 .unwrap();
809 root_element.children.contains(&self.id)
810 }
811
812 fn synthesize_presentational_hints_for_legacy_attributes<V>(
813 &self,
814 _visited_handling: VisitedHandlingMode,
815 hints: &mut V,
816 ) where
817 V: Push<style::applicable_declarations::ApplicableDeclarationBlock>,
818 {
819 let Some(elem) = self.data.downcast_element() else {
820 return;
821 };
822
823 let tag = &elem.name.local;
824
825 let mut push_style = |decl: PropertyDeclaration| {
826 hints.push(ApplicableDeclarationBlock::from_declarations(
827 Arc::new(
828 self.guard
829 .wrap(PropertyDeclarationBlock::with_one(decl, Importance::Normal)),
830 ),
831 CascadeLevel::new(CascadeOrigin::PresHints),
832 LayerOrder::root(),
833 ));
834 };
835
836 fn parse_color_attr(value: &str) -> Option<(u8, u8, u8, f32)> {
837 if !value.starts_with('#') {
838 return None;
839 }
840
841 let value = &value[1..];
842 if value.len() == 3 {
843 let r = u8::from_str_radix(&value[0..1], 16).ok()?;
844 let g = u8::from_str_radix(&value[1..2], 16).ok()?;
845 let b = u8::from_str_radix(&value[2..3], 16).ok()?;
846 return Some((r, g, b, 1.0));
847 }
848
849 if value.len() == 6 {
850 let r = u8::from_str_radix(&value[0..2], 16).ok()?;
851 let g = u8::from_str_radix(&value[2..4], 16).ok()?;
852 let b = u8::from_str_radix(&value[4..6], 16).ok()?;
853 return Some((r, g, b, 1.0));
854 }
855
856 None
857 }
858
859 fn parse_size_attr(
860 value: &str,
861 filter_fn: impl FnOnce(&f32) -> bool,
862 ) -> Option<style::values::specified::LengthPercentage> {
863 use style::values::specified::{LengthPercentage, NoCalcLength};
864 if let Some(value) = value.strip_suffix("px") {
865 let val: f32 = value.parse().ok()?;
866 return Some(LengthPercentage::Length(NoCalcLength::from_px(val)));
867 }
868
869 if let Some(value) = value.strip_suffix("%") {
870 let val: f32 = value.parse().ok()?;
871 return Some(LengthPercentage::Percentage(NoCalcPercentage::new(
872 val / 100.0,
873 )));
874 }
875
876 let val: f32 = value.parse().ok().filter(filter_fn)?;
877 Some(LengthPercentage::Length(NoCalcLength::from_px(val)))
878 }
879
880 fn parse_svg_size_attr(value: &str) -> Option<style::values::specified::LengthPercentage> {
885 use style::values::specified::{LengthPercentage, NoCalcLength};
886 use style_traits::ParsingMode;
887
888 let value = value.trim();
889 if let Some(number) = value.strip_suffix('%') {
890 let val: f32 = number.trim().parse().ok()?;
891 return (val >= 0.0)
892 .then(|| LengthPercentage::Percentage(NoCalcPercentage::new(val / 100.0)));
893 }
894
895 let number_len = value
899 .trim_end_matches(|c: char| c.is_ascii_alphabetic())
900 .len();
901 let (number, unit) = value.split_at(number_len);
902 let val: f32 = number.trim().parse().ok().filter(|v| *v >= 0.0)?;
903 let length = if unit.is_empty() {
904 NoCalcLength::from_px(val)
905 } else {
906 NoCalcLength::parse_dimension_with_flags(ParsingMode::DEFAULT, false, val, unit)
907 .ok()?
908 };
909 Some(LengthPercentage::Length(length))
910 }
911
912 for attr in elem.attrs() {
913 let name = &attr.name.local;
914 let value = attr.value.as_str();
915
916 if *name == local_name!("align") {
917 use style::values::specified::TextAlign;
918 let keyword = match value {
919 "left" => Some(StyloTextAlign::MozLeft),
920 "right" => Some(StyloTextAlign::MozRight),
921 "center" => Some(StyloTextAlign::MozCenter),
922 _ => None,
923 };
924
925 if let Some(keyword) = keyword {
926 push_style(PropertyDeclaration::TextAlign(TextAlign::Keyword(keyword)));
927 }
928 }
929
930 if *name == local_name!("width")
932 && (*tag == local_name!("table")
933 || *tag == local_name!("col")
934 || *tag == local_name!("tr")
935 || *tag == local_name!("td")
936 || *tag == local_name!("th")
937 || *tag == local_name!("hr"))
938 {
939 let is_table = *tag == local_name!("table");
940 if let Some(width) = parse_size_attr(value, |v| !is_table || *v != 0.0) {
941 use style::values::generics::{NonNegative, length::Size};
942
943 push_style(PropertyDeclaration::Width(Size::LengthPercentage(
944 NonNegative(width),
945 )));
946 }
947 }
948
949 if *name == local_name!("height")
950 && (*tag == local_name!("table")
951 || *tag == local_name!("thead")
952 || *tag == local_name!("tbody")
953 || *tag == local_name!("tfoot"))
954 {
955 if let Some(height) = parse_size_attr(value, |_| true) {
956 use style::values::generics::{NonNegative, length::Size};
957 push_style(PropertyDeclaration::Height(Size::LengthPercentage(
958 NonNegative(height),
959 )));
960 }
961 }
962
963 if *tag == local_name!("svg")
969 && (*name == local_name!("width") || *name == local_name!("height"))
970 {
971 if let Some(size) = parse_svg_size_attr(value) {
972 use style::values::generics::{NonNegative, length::Size};
973 let size = Size::LengthPercentage(NonNegative(size));
974 push_style(if *name == local_name!("width") {
975 PropertyDeclaration::Width(size)
976 } else {
977 PropertyDeclaration::Height(size)
978 });
979 }
980 }
981
982 if *name == local_name!("bgcolor") {
983 use style::values::specified::Color;
984 if let Some((r, g, b, a)) = parse_color_attr(value) {
985 push_style(PropertyDeclaration::BackgroundColor(
986 Color::from_absolute_color(AbsoluteColor::srgb_legacy(r, g, b, a)),
987 ));
988 }
989 }
990
991 if *name == local_name!("hidden") {
992 use style::values::specified::Display;
993 push_style(PropertyDeclaration::Display(Display::None));
994 }
995 }
996 }
997
998 fn local_name(&self) -> &LocalName {
999 &self.element_data().expect("Not an element").name.local
1000 }
1001
1002 fn namespace(&self) -> &Namespace {
1003 &self.element_data().expect("Not an element").name.ns
1004 }
1005
1006 fn query_container_size(
1007 &self,
1008 _display: &style::values::specified::Display,
1009 ) -> euclid::default::Size2D<Option<app_units::Au>> {
1010 Default::default()
1012 }
1013
1014 fn each_custom_state<F>(&self, _callback: F)
1015 where
1016 F: FnMut(&AtomIdent),
1017 {
1018 todo!()
1019 }
1020
1021 fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool {
1022 self.selector_flags.get().contains(flags)
1023 }
1024
1025 fn relative_selector_search_direction(&self) -> ElementSelectorFlags {
1026 let flags = self.selector_flags.get();
1027 if flags.contains(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING)
1028 {
1029 ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING
1030 } else if flags.contains(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR)
1031 {
1032 ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR
1033 } else if flags.contains(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING) {
1034 ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING
1035 } else {
1036 ElementSelectorFlags::empty()
1037 }
1038 }
1039
1040 fn compute_layout_damage(old: &ComputedValues, new: &ComputedValues) -> RestyleDamage {
1041 compute_layout_damage(old, new)
1042 }
1044
1045 }
1065
1066pub struct Traverser<'a> {
1067 parent: BlitzNode<'a>,
1069 child_index: usize,
1070}
1071
1072impl<'a> Iterator for Traverser<'a> {
1073 type Item = BlitzNode<'a>;
1074
1075 fn next(&mut self) -> Option<Self::Item> {
1076 let node_id = self.parent.children.get(self.child_index)?;
1077 let node = self.parent.with(*node_id);
1078
1079 self.child_index += 1;
1080
1081 Some(node)
1082 }
1083}
1084
1085impl std::hash::Hash for BlitzNode<'_> {
1086 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1087 state.write_usize(self.id)
1088 }
1089}
1090
1091pub struct RegisteredPaintersImpl;
1095impl RegisteredSpeculativePainters for RegisteredPaintersImpl {
1096 fn get(&self, _name: &Atom) -> Option<&dyn RegisteredSpeculativePainter> {
1097 None
1098 }
1099}
1100
1101use style::traversal::recalc_style_at;
1102
1103pub struct RecalcStyle<'a> {
1104 context: SharedStyleContext<'a>,
1105}
1106
1107impl<'a> RecalcStyle<'a> {
1108 pub fn new(context: SharedStyleContext<'a>) -> Self {
1109 RecalcStyle { context }
1110 }
1111}
1112
1113#[allow(unsafe_code)]
1114impl<E> DomTraversal<E> for RecalcStyle<'_>
1115where
1116 E: TElement,
1117{
1118 fn process_preorder<F: FnMut(E::ConcreteNode)>(
1119 &self,
1120 traversal_data: &PerLevelTraversalData,
1121 context: &mut StyleContext<E>,
1122 node: E::ConcreteNode,
1123 note_child: F,
1124 ) {
1125 if let Some(el) = node.as_element() {
1126 let mut data = unsafe { el.ensure_data() };
1128 recalc_style_at(self, traversal_data, context, el, &mut data, note_child);
1129
1130 unsafe { el.unset_dirty_descendants() }
1132 }
1133 }
1134
1135 #[inline]
1136 fn needs_postorder_traversal() -> bool {
1137 false
1138 }
1139
1140 fn process_postorder(&self, _style_context: &mut StyleContext<E>, _node: E::ConcreteNode) {
1141 panic!("this should never be called")
1142 }
1143
1144 #[inline]
1145 fn shared_context(&self) -> &SharedStyleContext<'_> {
1146 &self.context
1147 }
1148}
1149
1150#[test]
1151fn assert_size_of_equals() {
1152 }
1168
1169#[test]
1170fn parse_inline() {
1171 }