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