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