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