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