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