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::bloom::BLOOM_HASH_MASK;
14use selectors::{
15 Element, OpaqueElement,
16 attr::{AttrSelectorOperation, AttrSelectorOperator, NamespaceConstraint},
17 matching::{ElementSelectorFlags, MatchingContext, VisitedHandlingMode},
18 sink::Push,
19};
20use style::CaseSensitivityExt;
21use style::animation::AnimationSetKey;
22use style::animation::AnimationState;
23use style::applicable_declarations::ApplicableDeclarationBlock;
24use style::bloom::each_relevant_element_hash;
25use style::color::AbsoluteColor;
26use style::dom::AttributeProvider;
27use style::invalidation::element::restyle_hints::RestyleHint;
28use style::properties::ComputedValues;
29use style::properties::{Importance, PropertyDeclaration};
30use style::rule_tree::CascadeLevel;
31use style::selector_parser::PseudoElement;
32use style::selector_parser::RestyleDamage;
33use style::stylesheets::layer_rule::LayerOrder;
34use style::stylesheets::scope_rule::ImplicitScopeRoot;
35use style::values::AtomString;
36use style::values::computed::Percentage;
37use style::{
38 Atom,
39 context::{
40 QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters,
41 SharedStyleContext, StyleContext,
42 },
43 dom::{LayoutIterator, NodeInfo, OpaqueNode, TDocument, TElement, TNode, TShadowRoot},
44 global_style_data::GLOBAL_STYLE_DATA,
45 properties::PropertyDeclarationBlock,
46 selector_parser::{NonTSPseudoClass, SelectorImpl},
47 servo_arc::{Arc, ArcBorrow},
48 shared_lock::{Locked, SharedRwLock, StylesheetGuards},
49 thread_state::ThreadState,
50 traversal::{DomTraversal, PerLevelTraversalData},
51 traversal_flags::TraversalFlags,
52 values::{AtomIdent, GenericAtomIdent},
53};
54use style_dom::ElementState;
55
56use style::values::computed::text::TextAlign as StyloTextAlign;
57
58impl crate::document::BaseDocument {
59 fn build_shadow_cascade_data(&mut self) {
62 use style::stylesheet_set::DocumentStylesheetSet;
63
64 if self.shadow_scoped_sheets.is_empty() {
65 return;
66 }
67
68 let shadow_root_ids: Vec<usize> = self.shadow_scoped_sheets.keys().copied().collect();
69 for shadow_root_id in shadow_root_ids {
70 let sheets = match self.shadow_scoped_sheets.get(&shadow_root_id) {
71 Some(s) if !s.is_empty() => s.clone(),
72 _ => continue,
73 };
74
75 let mut set = DocumentStylesheetSet::<style::stylesheets::DocumentStyleSheet>::new();
76 for sheet in &sheets {
77 let guard = self.guard.read();
78 set.append_stylesheet(
79 Some(self.stylist.device()),
80 &Default::default(),
81 sheet.clone(),
82 &guard,
83 );
84 }
85
86 let guard = self.guard.read();
87 let mut flusher = set.flush::<BlissNode>(None, None);
88 let author_flusher = flusher.flush_origin(style::stylesheets::origin::Origin::Author);
89 let mut cascade_data = style::stylist::CascadeData::new();
90 if cascade_data
91 .rebuild(
92 self.stylist.device(),
93 style::context::QuirksMode::NoQuirks,
94 author_flusher,
95 &guard,
96 )
97 .is_ok()
98 {
99 self.shadow_cascade_data
100 .insert(shadow_root_id, Box::new(cascade_data));
101 }
102 }
103 }
104
105 pub fn resolve_stylist(&mut self, now: f64) {
106 style::thread_state::enter(ThreadState::LAYOUT);
107
108 {
115 let guard = self.guard.read();
116 let guards = StylesheetGuards {
117 author: &guard,
118 ua_or_user: &guard,
119 };
120
121 let root = TDocument::as_node(&&self.nodes[0])
122 .first_element_child()
123 .unwrap()
124 .as_element()
125 .unwrap();
126
127 self.stylist
128 .flush(&guards, Some(root), Some(&self.snapshots));
129 }
130
131 self.build_shadow_cascade_data();
133
134 let mut sets = self.animations.sets.write();
138 for (key, set) in sets.iter_mut() {
139 let node_id = key.node.id();
140 self.nodes[node_id].set_restyle_hint(RestyleHint::RESTYLE_SELF);
141
142 for animation in set.animations.iter_mut() {
143 if animation.state == AnimationState::Pending && animation.started_at <= now {
144 animation.state = AnimationState::Running;
145 }
146 animation.iterate_if_necessary(now);
147
148 if animation.state == AnimationState::Running && animation.has_ended(now) {
149 animation.state = AnimationState::Finished;
150 }
151 }
152
153 for transition in set.transitions.iter_mut() {
154 if transition.state == AnimationState::Pending && transition.start_time <= now {
155 transition.state = AnimationState::Running;
156 }
157 if transition.state == AnimationState::Running && transition.has_ended(now) {
158 transition.state = AnimationState::Finished;
159 }
160 }
161 }
162 drop(sets);
163
164 {
170 let guard = self.guard.read();
171 let guards = StylesheetGuards {
172 author: &guard,
173 ua_or_user: &guard,
174 };
175
176 let context = SharedStyleContext {
177 traversal_flags: TraversalFlags::empty(),
178 stylist: &self.stylist,
179 options: GLOBAL_STYLE_DATA.options.clone(),
180 guards,
181 visited_styles_enabled: false,
182 animations: self.animations.clone(),
183 current_time_for_animations: now,
184 snapshot_map: &self.snapshots,
185 registered_speculative_painters: &RegisteredPaintersImpl,
186 };
187
188 if let Some(root) = self.root_element() {
193 let token = RecalcStyle::pre_traverse(root, &context);
194
195 if token.should_traverse() {
196 let traverser = RecalcStyle::new(context);
198 style::driver::traverse_dom(&traverser, token, None);
199 }
200 }
201 }
202
203 for opaque in self.snapshots.keys() {
204 let id = opaque.id();
205 if let Some(node) = self.nodes.get_mut(id) {
206 node.has_snapshot = false;
207 }
208 }
209 self.snapshots.clear();
210
211 let mut sets = self.animations.sets.write();
212 for set in sets.values_mut() {
213 set.clear_canceled_animations();
214 for animation in set.animations.iter_mut() {
215 animation.is_new = false;
216 }
217 for transition in set.transitions.iter_mut() {
218 transition.is_new = false;
219 }
220 }
221 sets.retain(|_, state| !state.is_empty());
222 self.has_active_animations = sets.values().any(|state| state.needs_animation_ticks());
223
224 style::thread_state::exit(ThreadState::LAYOUT);
225 }
226}
227
228type BlissNode<'a> = &'a Node;
233
234impl<'a> TDocument for BlissNode<'a> {
235 type ConcreteNode = BlissNode<'a>;
236
237 fn as_node(&self) -> Self::ConcreteNode {
238 self
239 }
240
241 fn is_html_document(&self) -> bool {
242 true
243 }
244
245 fn quirks_mode(&self) -> QuirksMode {
246 QuirksMode::NoQuirks
247 }
248
249 fn shared_lock(&self) -> &SharedRwLock {
250 &self.guard
251 }
252}
253
254impl NodeInfo for BlissNode<'_> {
255 fn is_element(&self) -> bool {
256 Node::is_element(self)
257 }
258
259 fn is_text_node(&self) -> bool {
260 Node::is_text_node(self)
261 }
262}
263
264impl<'a> TShadowRoot for BlissNode<'a> {
265 type ConcreteNode = BlissNode<'a>;
266
267 fn as_node(&self) -> Self::ConcreteNode {
268 self
269 }
270
271 fn host(&self) -> <Self::ConcreteNode as TNode>::ConcreteElement {
272 if let NodeData::ShadowRoot { host } = self.data {
273 self.tree().get(host).unwrap_or(self)
276 } else {
277 self
279 }
280 }
281
282 fn style_data<'b>(&self) -> Option<&'b style::stylist::CascadeData>
283 where
284 Self: 'b,
285 {
286 let doc = self.doc();
290 doc.shadow_cascade_data
291 .get(&self.id)
292 .map(|boxed| &**boxed as &style::stylist::CascadeData)
293 }
294}
295
296impl<'a> TNode for BlissNode<'a> {
298 type ConcreteElement = BlissNode<'a>;
299 type ConcreteDocument = BlissNode<'a>;
300 type ConcreteShadowRoot = BlissNode<'a>;
301
302 fn parent_node(&self) -> Option<Self> {
303 self.parent.map(|id| self.with(id))
304 }
305
306 fn first_child(&self) -> Option<Self> {
307 self.children.first().map(|id| self.with(*id))
308 }
309
310 fn last_child(&self) -> Option<Self> {
311 self.children.last().map(|id| self.with(*id))
312 }
313
314 fn prev_sibling(&self) -> Option<Self> {
315 self.backward(1)
316 }
317
318 fn next_sibling(&self) -> Option<Self> {
319 self.forward(1)
320 }
321
322 fn owner_doc(&self) -> Self::ConcreteDocument {
323 self.with(1)
324 }
325
326 fn is_in_document(&self) -> bool {
327 true
328 }
329
330 fn traversal_parent(&self) -> Option<Self::ConcreteElement> {
335 self.parent_node().and_then(|node| node.as_element())
336 }
337
338 fn opaque(&self) -> OpaqueNode {
339 OpaqueNode(self.id)
340 }
341
342 fn debug_id(self) -> usize {
343 self.id
344 }
345
346 fn as_element(&self) -> Option<Self::ConcreteElement> {
347 match self.data {
348 NodeData::Element { .. } => Some(self),
349 _ => None,
350 }
351 }
352
353 fn as_document(&self) -> Option<Self::ConcreteDocument> {
354 match self.data {
355 NodeData::Document => Some(self),
356 _ => None,
357 }
358 }
359
360 fn as_shadow_root(&self) -> Option<Self::ConcreteShadowRoot> {
361 if matches!(self.data, NodeData::ShadowRoot { .. }) {
362 Some(self)
363 } else {
364 None
365 }
366 }
367}
368
369impl AttributeProvider for BlissNode<'_> {
370 fn get_attr(&self, attr: &style::LocalName) -> Option<String> {
371 self.attr(attr.0.clone()).map(|s| s.to_string())
372 }
373}
374
375impl selectors::Element for BlissNode<'_> {
376 type Impl = SelectorImpl;
377
378 fn opaque(&self) -> selectors::OpaqueElement {
379 let non_null = NonNull::new((self.id + 1) as *mut ()).unwrap();
387 OpaqueElement::from_non_null_ptr(non_null)
388 }
389
390 fn parent_element(&self) -> Option<Self> {
391 TElement::traversal_parent(self)
392 }
393
394 fn parent_node_is_shadow_root(&self) -> bool {
395 self.parent_node()
396 .map(|parent| matches!(parent.data, NodeData::ShadowRoot { .. }))
397 .unwrap_or(false)
398 }
399
400 fn containing_shadow_host(&self) -> Option<Self> {
401 let mut current = self.parent_node();
402 while let Some(parent) = current {
403 if let NodeData::ShadowRoot { host } = parent.data {
404 return Some(self.with(host));
405 }
406 current = parent.parent_node();
407 }
408 None
409 }
410
411 fn is_pseudo_element(&self) -> bool {
412 matches!(self.data, NodeData::AnonymousBlock(_))
413 }
414
415 fn prev_sibling_element(&self) -> Option<Self> {
418 let mut n = 1;
419 while let Some(node) = self.backward(n) {
420 if node.is_element() {
421 return Some(node);
422 }
423 n += 1;
424 }
425
426 None
427 }
428
429 fn next_sibling_element(&self) -> Option<Self> {
430 let mut n = 1;
431 while let Some(node) = self.forward(n) {
432 if node.is_element() {
433 return Some(node);
434 }
435 n += 1;
436 }
437
438 None
439 }
440
441 fn first_element_child(&self) -> Option<Self> {
442 let mut children = self.dom_children();
443 children.find(|child| child.is_element())
444 }
445
446 fn is_html_element_in_html_document(&self) -> bool {
447 true }
449
450 fn has_local_name(&self, local_name: &LocalName) -> bool {
451 self.data.is_element_with_tag_name(local_name)
452 }
453
454 fn has_namespace(&self, ns: &Namespace) -> bool {
455 self.element_data().expect("Not an element").name.ns == *ns
456 }
457
458 fn is_same_type(&self, other: &Self) -> bool {
459 self.local_name() == other.local_name() && self.namespace() == other.namespace()
460 }
461
462 fn attr_matches(
463 &self,
464 _ns: &NamespaceConstraint<&GenericAtomIdent<NamespaceStaticSet>>,
465 local_name: &GenericAtomIdent<LocalNameStaticSet>,
466 operation: &AttrSelectorOperation<&AtomString>,
467 ) -> bool {
468 let Some(attr_value) = self.data.attr(local_name.0.clone()) else {
469 return false;
470 };
471
472 match operation {
473 AttrSelectorOperation::Exists => true,
474 AttrSelectorOperation::WithValue {
475 operator,
476 case_sensitivity: _,
477 value,
478 } => {
479 let value = value.as_ref();
480
481 match operator {
483 AttrSelectorOperator::Equal => attr_value == value,
484 AttrSelectorOperator::Includes => attr_value
485 .split_ascii_whitespace()
486 .any(|word| word == value),
487 AttrSelectorOperator::DashMatch => {
488 attr_value.starts_with(value)
491 && (attr_value.len() == value.len()
492 || attr_value.chars().nth(value.len()) == Some('-'))
493 }
494 AttrSelectorOperator::Prefix => attr_value.starts_with(value),
495 AttrSelectorOperator::Substring => attr_value.contains(value),
496 AttrSelectorOperator::Suffix => attr_value.ends_with(value),
497 }
498 }
499 }
500 }
501
502 fn match_non_ts_pseudo_class(
503 &self,
504 pseudo_class: &<Self::Impl as selectors::SelectorImpl>::NonTSPseudoClass,
505 _context: &mut MatchingContext<Self::Impl>,
506 ) -> bool {
507 match *pseudo_class {
508 NonTSPseudoClass::Active => self.element_state.contains(ElementState::ACTIVE),
509 NonTSPseudoClass::AnyLink => self
510 .data
511 .downcast_element()
512 .map(|elem| {
513 (elem.name.local == local_name!("a") || elem.name.local == local_name!("area"))
514 && elem.attr(local_name!("href")).is_some()
515 })
516 .unwrap_or(false),
517 NonTSPseudoClass::Checked => self
518 .data
519 .downcast_element()
520 .and_then(|elem| elem.checkbox_input_checked())
521 .unwrap_or(false),
522 NonTSPseudoClass::Valid => false,
523 NonTSPseudoClass::Invalid => false,
524 NonTSPseudoClass::Defined => false,
525 NonTSPseudoClass::Disabled => self.element_state.contains(ElementState::DISABLED),
526 NonTSPseudoClass::Enabled => self.element_state.contains(ElementState::ENABLED),
527 NonTSPseudoClass::Focus => self.element_state.contains(ElementState::FOCUS),
528 NonTSPseudoClass::FocusWithin => false,
529 NonTSPseudoClass::FocusVisible => false,
530 NonTSPseudoClass::Fullscreen => false,
531 NonTSPseudoClass::Hover => self.element_state.contains(ElementState::HOVER),
532 NonTSPseudoClass::Indeterminate => false,
533 NonTSPseudoClass::Lang(_) => false,
534 NonTSPseudoClass::CustomState(_) => false,
535 NonTSPseudoClass::Link => self
536 .data
537 .downcast_element()
538 .map(|elem| {
539 (elem.name.local == local_name!("a") || elem.name.local == local_name!("area"))
540 && elem.attr(local_name!("href")).is_some()
541 })
542 .unwrap_or(false),
543 NonTSPseudoClass::PlaceholderShown => false,
544 NonTSPseudoClass::ReadWrite => false,
545 NonTSPseudoClass::ReadOnly => false,
546 NonTSPseudoClass::ServoNonZeroBorder => false,
547 NonTSPseudoClass::Target => false,
548 NonTSPseudoClass::Visited => false,
549 NonTSPseudoClass::Autofill => false,
550 NonTSPseudoClass::Default => false,
551
552 NonTSPseudoClass::InRange => false,
553 NonTSPseudoClass::Modal => false,
554 NonTSPseudoClass::Optional => false,
555 NonTSPseudoClass::OutOfRange => false,
556 NonTSPseudoClass::PopoverOpen => false,
557 NonTSPseudoClass::Required => false,
558 NonTSPseudoClass::UserInvalid => false,
559 NonTSPseudoClass::UserValid => false,
560 NonTSPseudoClass::MozMeterOptimum => false,
561 NonTSPseudoClass::MozMeterSubOptimum => false,
562 NonTSPseudoClass::MozMeterSubSubOptimum => false,
563 }
564 }
565
566 fn match_pseudo_element(
567 &self,
568 pe: &PseudoElement,
569 _context: &mut MatchingContext<Self::Impl>,
570 ) -> bool {
571 match self.data {
572 NodeData::AnonymousBlock(_) => *pe == PseudoElement::ServoAnonymousBox,
573 _ => false,
574 }
575 }
576
577 fn apply_selector_flags(&self, flags: ElementSelectorFlags) {
578 let self_flags = flags.for_self();
580 if !self_flags.is_empty() {
581 *self.selector_flags.borrow_mut() |= self_flags;
582 }
583
584 let parent_flags = flags.for_parent();
586 if !parent_flags.is_empty() {
587 if let Some(parent) = self.parent_node() {
588 *parent.selector_flags.borrow_mut() |= parent_flags;
589 }
590 }
591 }
592
593 fn is_link(&self) -> bool {
594 self.data.is_element_with_tag_name(&local_name!("a"))
595 }
596
597 fn is_html_slot_element(&self) -> bool {
598 self.data.is_element_with_tag_name(&local_name!("slot"))
599 }
600
601 fn has_id(
602 &self,
603 id: &<Self::Impl as selectors::SelectorImpl>::Identifier,
604 case_sensitivity: selectors::attr::CaseSensitivity,
605 ) -> bool {
606 self.element_data()
607 .and_then(|data| data.id.as_ref())
608 .map(|id_attr| case_sensitivity.eq_atom(id_attr, id))
609 .unwrap_or(false)
610 }
611
612 fn has_class(
613 &self,
614 search_name: &<Self::Impl as selectors::SelectorImpl>::Identifier,
615 case_sensitivity: selectors::attr::CaseSensitivity,
616 ) -> bool {
617 let class_attr = self.data.attr(local_name!("class"));
618 if let Some(class_attr) = class_attr {
619 for pheme in class_attr.split_ascii_whitespace() {
621 let atom = Atom::from(pheme);
622 if case_sensitivity.eq_atom(&atom, search_name) {
623 return true;
624 }
625 }
626 }
627
628 false
629 }
630
631 fn imported_part(
632 &self,
633 _name: &<Self::Impl as selectors::SelectorImpl>::Identifier,
634 ) -> Option<<Self::Impl as selectors::SelectorImpl>::Identifier> {
635 None
636 }
637
638 fn is_part(&self, _name: &<Self::Impl as selectors::SelectorImpl>::Identifier) -> bool {
639 false
640 }
641
642 fn is_empty(&self) -> bool {
643 self.dom_children().next().is_none()
644 }
645
646 fn is_root(&self) -> bool {
647 self.parent_node()
648 .and_then(|parent| parent.parent_node())
649 .is_none()
650 }
651
652 fn has_custom_state(
653 &self,
654 _name: &<Self::Impl as selectors::SelectorImpl>::Identifier,
655 ) -> bool {
656 false
657 }
658
659 fn add_element_unique_hashes(&self, filter: &mut selectors::bloom::BloomFilter) -> bool {
660 each_relevant_element_hash(*self, |hash| filter.insert_hash(hash & BLOOM_HASH_MASK));
661 true
662 }
663}
664
665impl<'a> TElement for BlissNode<'a> {
666 type ConcreteNode = BlissNode<'a>;
667
668 type TraversalChildrenIterator = Traverser<'a>;
669
670 fn as_node(&self) -> Self::ConcreteNode {
671 self
672 }
673
674 fn implicit_scope_for_sheet_in_shadow_root(
675 _opaque_host: OpaqueElement,
676 _sheet_index: usize,
677 ) -> Option<ImplicitScopeRoot> {
678 None
683 }
684
685 fn traversal_children(&self) -> style::dom::LayoutIterator<Self::TraversalChildrenIterator> {
686 LayoutIterator(Traverser {
687 parent: self,
689 child_index: 0,
690 })
691 }
692
693 fn is_html_element(&self) -> bool {
694 self.is_element()
695 }
696
697 fn is_mathml_element(&self) -> bool {
699 false
700 }
701
702 fn is_svg_element(&self) -> bool {
704 false
705 }
706
707 fn style_attribute(&self) -> Option<ArcBorrow<'_, Locked<PropertyDeclarationBlock>>> {
708 self.element_data()
709 .expect("Not an element")
710 .style_attribute
711 .as_ref()
712 .map(|f| f.borrow_arc())
713 }
714
715 fn state(&self) -> ElementState {
716 self.element_state
717 }
718
719 fn has_part_attr(&self) -> bool {
720 false
721 }
722
723 fn exports_any_part(&self) -> bool {
724 false
725 }
726
727 fn id(&self) -> Option<&style::Atom> {
728 self.element_data().and_then(|data| data.id.as_ref())
729 }
730
731 fn each_class<F>(&self, mut callback: F)
732 where
733 F: FnMut(&style::values::AtomIdent),
734 {
735 let class_attr = self.data.attr(local_name!("class"));
736 if let Some(class_attr) = class_attr {
737 for pheme in class_attr.split_ascii_whitespace() {
739 let atom = Atom::from(pheme); callback(AtomIdent::cast(&atom));
741 }
742 }
743 }
744
745 fn each_attr_name<F>(&self, mut callback: F)
746 where
747 F: FnMut(&style::LocalName),
748 {
749 if let Some(attrs) = self.data.attrs() {
750 for attr in attrs.iter() {
751 callback(&GenericAtomIdent(attr.name.local.clone()));
752 }
753 }
754 }
755
756 fn has_dirty_descendants(&self) -> bool {
757 Node::has_dirty_descendants(self)
758 }
759
760 fn has_snapshot(&self) -> bool {
761 self.has_snapshot
762 }
763
764 fn handled_snapshot(&self) -> bool {
765 self.snapshot_handled.load(Ordering::SeqCst)
766 }
767
768 unsafe fn set_handled_snapshot(&self) {
769 self.snapshot_handled.store(true, Ordering::SeqCst);
770 }
771
772 unsafe fn set_dirty_descendants(&self) {
773 Node::set_dirty_descendants(self);
774 Node::mark_ancestors_dirty(self);
775 }
776
777 unsafe fn unset_dirty_descendants(&self) {
778 Node::unset_dirty_descendants(self);
779 }
780
781 fn store_children_to_process(&self, _n: isize) {
782 }
784
785 fn did_process_child(&self) -> isize {
786 0
787 }
788
789 unsafe fn ensure_data(&self) -> AtomicRefMut<'_, style::data::ElementData> {
790 let mut stylo_data = self.stylo_element_data.borrow_mut();
791 if stylo_data.is_none() {
792 *stylo_data = Some(style::data::ElementData {
793 damage: ALL_DAMAGE,
794 ..Default::default()
795 });
796 }
797 AtomicRefMut::map(stylo_data, |sd| sd.as_mut().unwrap())
798 }
799
800 unsafe fn clear_data(&self) {
801 *self.stylo_element_data.borrow_mut() = None;
802 }
803
804 fn has_data(&self) -> bool {
805 self.stylo_element_data.borrow().is_some()
806 }
807
808 fn borrow_data(&self) -> Option<AtomicRef<'_, style::data::ElementData>> {
809 let stylo_data = self.stylo_element_data.borrow();
810 if stylo_data.is_some() {
811 Some(AtomicRef::map(stylo_data, |sd| sd.as_ref().unwrap()))
812 } else {
813 None
814 }
815 }
816
817 fn mutate_data(&self) -> Option<AtomicRefMut<'_, style::data::ElementData>> {
818 let stylo_data = self.stylo_element_data.borrow_mut();
819 if stylo_data.is_some() {
820 Some(AtomicRefMut::map(stylo_data, |sd| sd.as_mut().unwrap()))
821 } else {
822 None
823 }
824 }
825
826 fn skip_item_display_fixup(&self) -> bool {
827 false
828 }
829
830 fn may_have_animations(&self) -> bool {
831 true
832 }
833
834 fn has_animations(&self, context: &SharedStyleContext) -> bool {
835 self.has_css_animations(context, None) || self.has_css_transitions(context, None)
836 }
837
838 fn has_css_animations(
839 &self,
840 context: &SharedStyleContext,
841 pseudo_element: Option<PseudoElement>,
842 ) -> bool {
843 let key = AnimationSetKey::new(TNode::opaque(&TElement::as_node(self)), pseudo_element);
844 context.animations.has_active_animations(&key)
845 }
846
847 fn has_css_transitions(
848 &self,
849 context: &SharedStyleContext,
850 pseudo_element: Option<PseudoElement>,
851 ) -> bool {
852 let key = AnimationSetKey::new(TNode::opaque(&TElement::as_node(self)), pseudo_element);
853 context.animations.has_active_transitions(&key)
854 }
855
856 fn animation_rule(
857 &self,
858 context: &SharedStyleContext,
859 ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
860 let opaque = TNode::opaque(&TElement::as_node(self));
861 context.animations.get_animation_declarations(
862 &AnimationSetKey::new_for_non_pseudo(opaque),
863 context.current_time_for_animations,
864 &self.guard,
865 )
866 }
867
868 fn transition_rule(
869 &self,
870 context: &SharedStyleContext,
871 ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
872 let opaque = TNode::opaque(&TElement::as_node(self));
873 context.animations.get_transition_declarations(
874 &AnimationSetKey::new_for_non_pseudo(opaque),
875 context.current_time_for_animations,
876 &self.guard,
877 )
878 }
879
880 fn shadow_root(&self) -> Option<<Self::ConcreteNode as TNode>::ConcreteShadowRoot> {
881 self.children
882 .iter()
883 .find(|&&child_id| self.with(child_id).is_shadow_root())
884 .map(|&child_id| self.with(child_id))
885 }
886
887 fn containing_shadow(&self) -> Option<<Self::ConcreteNode as TNode>::ConcreteShadowRoot> {
888 let mut current = self.parent_node();
889 while let Some(parent) = current {
890 if matches!(parent.data, NodeData::ShadowRoot { .. }) {
891 return Some(parent);
892 }
893 current = parent.parent_node();
894 }
895 None
896 }
897
898 fn lang_attr(&self) -> Option<style::selector_parser::AttrValue> {
899 None
900 }
901
902 fn match_element_lang(
903 &self,
904 _override_lang: Option<Option<style::selector_parser::AttrValue>>,
905 _value: &style::selector_parser::Lang,
906 ) -> bool {
907 false
908 }
909
910 fn is_html_document_body_element(&self) -> bool {
911 let is_body_element = self.data.is_element_with_tag_name(&local_name!("body"));
913
914 if !is_body_element {
916 return false;
917 }
918
919 let root_node = &self.tree()[0];
921 let root_element = TDocument::as_node(&root_node)
922 .first_element_child()
923 .unwrap();
924 root_element.children.contains(&self.id)
925 }
926
927 fn synthesize_presentational_hints_for_legacy_attributes<V>(
928 &self,
929 _visited_handling: VisitedHandlingMode,
930 hints: &mut V,
931 ) where
932 V: Push<style::applicable_declarations::ApplicableDeclarationBlock>,
933 {
934 let Some(elem) = self.data.downcast_element() else {
935 return;
936 };
937
938 let tag = &elem.name.local;
939
940 let mut push_style = |decl: PropertyDeclaration| {
941 hints.push(ApplicableDeclarationBlock::from_declarations(
942 Arc::new(
943 self.guard
944 .wrap(PropertyDeclarationBlock::with_one(decl, Importance::Normal)),
945 ),
946 CascadeLevel::PresHints,
947 LayerOrder::root(),
948 ));
949 };
950
951 fn parse_color_attr(value: &str) -> Option<(u8, u8, u8, f32)> {
952 if !value.starts_with('#') {
953 return None;
954 }
955
956 let value = &value[1..];
957 if value.len() == 3 {
958 let r = u8::from_str_radix(&value[0..1], 16).ok()?;
959 let g = u8::from_str_radix(&value[1..2], 16).ok()?;
960 let b = u8::from_str_radix(&value[2..3], 16).ok()?;
961 return Some((r, g, b, 1.0));
962 }
963
964 if value.len() == 6 {
965 let r = u8::from_str_radix(&value[0..2], 16).ok()?;
966 let g = u8::from_str_radix(&value[2..4], 16).ok()?;
967 let b = u8::from_str_radix(&value[4..6], 16).ok()?;
968 return Some((r, g, b, 1.0));
969 }
970
971 None
972 }
973
974 fn parse_size_attr(
975 value: &str,
976 filter_fn: impl FnOnce(&f32) -> bool,
977 ) -> Option<style::values::specified::LengthPercentage> {
978 use style::values::specified::{AbsoluteLength, LengthPercentage, NoCalcLength};
979 if let Some(value) = value.strip_suffix("px") {
980 let val: f32 = value.parse().ok()?;
981 return Some(LengthPercentage::Length(NoCalcLength::Absolute(
982 AbsoluteLength::Px(val),
983 )));
984 }
985
986 if let Some(value) = value.strip_suffix("%") {
987 let val: f32 = value.parse().ok()?;
988 return Some(LengthPercentage::Percentage(Percentage(val / 100.0)));
989 }
990
991 let val: f32 = value.parse().ok().filter(filter_fn)?;
992 Some(LengthPercentage::Length(NoCalcLength::Absolute(
993 AbsoluteLength::Px(val),
994 )))
995 }
996
997 for attr in elem.attrs() {
998 let name = &attr.name.local;
999 let value = attr.value.as_str();
1000
1001 if *name == local_name!("align") {
1002 use style::values::specified::TextAlign;
1003 let keyword = match value {
1004 "left" => Some(StyloTextAlign::MozLeft),
1005 "right" => Some(StyloTextAlign::MozRight),
1006 "center" => Some(StyloTextAlign::MozCenter),
1007 _ => None,
1008 };
1009
1010 if let Some(keyword) = keyword {
1011 push_style(PropertyDeclaration::TextAlign(TextAlign::Keyword(keyword)));
1012 }
1013 }
1014
1015 if *name == local_name!("width")
1017 && (*tag == local_name!("table")
1018 || *tag == local_name!("col")
1019 || *tag == local_name!("tr")
1020 || *tag == local_name!("td")
1021 || *tag == local_name!("th")
1022 || *tag == local_name!("hr"))
1023 {
1024 let is_table = *tag == local_name!("table");
1025 if let Some(width) = parse_size_attr(value, |v| !is_table || *v != 0.0) {
1026 use style::values::generics::{NonNegative, length::Size};
1027
1028 push_style(PropertyDeclaration::Width(Size::LengthPercentage(
1029 NonNegative(width),
1030 )));
1031 }
1032 }
1033
1034 if *name == local_name!("height")
1035 && (*tag == local_name!("table")
1036 || *tag == local_name!("thead")
1037 || *tag == local_name!("tbody")
1038 || *tag == local_name!("tfoot"))
1039 {
1040 if let Some(height) = parse_size_attr(value, |_| true) {
1041 use style::values::generics::{NonNegative, length::Size};
1042 push_style(PropertyDeclaration::Height(Size::LengthPercentage(
1043 NonNegative(height),
1044 )));
1045 }
1046 }
1047
1048 if *name == local_name!("bgcolor") {
1049 use style::values::specified::Color;
1050 if let Some((r, g, b, a)) = parse_color_attr(value) {
1051 push_style(PropertyDeclaration::BackgroundColor(
1052 Color::from_absolute_color(AbsoluteColor::srgb_legacy(r, g, b, a)),
1053 ));
1054 }
1055 }
1056
1057 if *name == local_name!("hidden") {
1058 use style::values::specified::Display;
1059 push_style(PropertyDeclaration::Display(Display::None));
1060 }
1061 }
1062 }
1063
1064 fn local_name(&self) -> &LocalName {
1065 &self.element_data().expect("Not an element").name.local
1066 }
1067
1068 fn namespace(&self) -> &Namespace {
1069 &self.element_data().expect("Not an element").name.ns
1070 }
1071
1072 fn query_container_size(
1073 &self,
1074 display: &style::values::specified::Display,
1075 ) -> euclid::default::Size2D<Option<app_units::Au>> {
1076 use style::values::specified::box_::DisplayInside;
1078 if matches!(display.inside(), DisplayInside::Contents) {
1079 return euclid::default::Size2D::new(None, None);
1080 }
1081
1082 let Some(style) = self.primary_styles() else {
1084 return euclid::default::Size2D::new(None, None);
1085 };
1086 let ct = style.clone_container_type();
1087 use style::properties::longhands::container_type::computed_value::T as ContainerType;
1088 if ct == ContainerType::NORMAL {
1089 return euclid::default::Size2D::new(None, None);
1090 }
1091
1092 use app_units::Au;
1096 let raw = self.final_layout.size;
1097 let pad = self.final_layout.padding;
1098 let bor = self.final_layout.border;
1099 let inner_w = (raw.width - pad.left - pad.right - bor.left - bor.right).max(0.0);
1100 let inner_h = (raw.height - pad.top - pad.bottom - bor.top - bor.bottom).max(0.0);
1101
1102 euclid::default::Size2D::new(
1103 if inner_w > 0.0 {
1104 Some(Au((inner_w * 60.0f32) as i32))
1105 } else {
1106 None
1107 },
1108 if inner_h > 0.0 {
1109 Some(Au((inner_h * 60.0f32) as i32))
1110 } else {
1111 None
1112 },
1113 )
1114 }
1115
1116 fn each_custom_state<F>(&self, _callback: F)
1117 where
1118 F: FnMut(&AtomIdent),
1119 {
1120 }
1122
1123 fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool {
1124 self.selector_flags.borrow().contains(flags)
1125 }
1126
1127 fn relative_selector_search_direction(&self) -> ElementSelectorFlags {
1128 let flags = self.selector_flags.borrow();
1129 if flags.contains(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING)
1130 {
1131 ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING
1132 } else if flags.contains(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR)
1133 {
1134 ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR
1135 } else if flags.contains(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING) {
1136 ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING
1137 } else {
1138 ElementSelectorFlags::empty()
1139 }
1140 }
1141
1142 fn compute_layout_damage(old: &ComputedValues, new: &ComputedValues) -> RestyleDamage {
1143 compute_layout_damage(old, new)
1144 }
1146
1147 }
1167
1168pub struct Traverser<'a> {
1169 parent: BlissNode<'a>,
1171 child_index: usize,
1172}
1173
1174impl<'a> Iterator for Traverser<'a> {
1175 type Item = BlissNode<'a>;
1176
1177 fn next(&mut self) -> Option<Self::Item> {
1178 let node_id = self.parent.children.get(self.child_index)?;
1179 let node = self.parent.with(*node_id);
1180
1181 self.child_index += 1;
1182
1183 Some(node)
1184 }
1185}
1186
1187impl std::hash::Hash for BlissNode<'_> {
1188 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1189 state.write_usize(self.id)
1190 }
1191}
1192
1193pub struct RegisteredPaintersImpl;
1197impl RegisteredSpeculativePainters for RegisteredPaintersImpl {
1198 fn get(&self, _name: &Atom) -> Option<&dyn RegisteredSpeculativePainter> {
1199 None
1200 }
1201}
1202
1203use style::traversal::recalc_style_at;
1204
1205pub struct RecalcStyle<'a> {
1206 context: SharedStyleContext<'a>,
1207}
1208
1209impl<'a> RecalcStyle<'a> {
1210 pub fn new(context: SharedStyleContext<'a>) -> Self {
1211 RecalcStyle { context }
1212 }
1213}
1214
1215#[allow(unsafe_code)]
1216impl<E> DomTraversal<E> for RecalcStyle<'_>
1217where
1218 E: TElement,
1219{
1220 fn process_preorder<F: FnMut(E::ConcreteNode)>(
1221 &self,
1222 traversal_data: &PerLevelTraversalData,
1223 context: &mut StyleContext<E>,
1224 node: E::ConcreteNode,
1225 note_child: F,
1226 ) {
1227 if node.is_text_node() {
1229 return;
1230 }
1231
1232 let el = node.as_element().unwrap();
1233 let mut data = unsafe { el.ensure_data() };
1235 recalc_style_at(self, traversal_data, context, el, &mut data, note_child);
1236
1237 unsafe { el.unset_dirty_descendants() }
1239 }
1240
1241 #[inline]
1242 fn needs_postorder_traversal() -> bool {
1243 false
1244 }
1245
1246 fn process_postorder(&self, _style_context: &mut StyleContext<E>, _node: E::ConcreteNode) {
1247 panic!("this should never be called")
1248 }
1249
1250 #[inline]
1251 fn shared_context(&self) -> &SharedStyleContext<'_> {
1252 &self.context
1253 }
1254}
1255
1256#[test]
1257fn assert_size_of_equals() {
1258 }
1274
1275#[test]
1276fn parse_inline() {
1277 }