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