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