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