Skip to main content

bliss_dom/
stylo.rs

1//! Enable the dom to participate in styling by servo
2//!
3
4use 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    /// Build (or rebuild) CascadeData for each shadow root from its scoped stylesheets.
60    /// Called during `resolve_stylist()` after the stylist flush, before the style traversal.
61    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        // Phase 1: Flush the stylist's own cascade data.
109        // The read guard is scoped to this block so its borrow of `self` is
110        // released before the `&mut self` work in phases 2/3.
111        // (A second, separate read guard is acquired in Phase 4 below —
112        // both are needed because the `&mut self` work in between forces
113        // us to drop the guard before re-acquiring.)
114        {
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        // Phase 2: Build per-shadow-root cascade data from scoped stylesheets.
132        self.build_shadow_cascade_data();
133
134        // Phase 3: Mark actively animating nodes as dirty.
135
136        // Mark actively animating nodes as dirty
137        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        // Build the style context used by the style traversal.
165        // The read guard is scoped to this block so it lives through the
166        // traversal and is released before the `&mut self` cleanup below.
167        // (See Phase 1 above for why the guard is re-acquired here rather
168        // than hoisted to function scope.)
169        {
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            // components/layout_2020/lib.rs:983
189            // D-2c-followup: root_element widened to Option<&Node>. Skip the
190            // entire style traversal when there is no root element to root
191            // from — pre_traverse requires a ConcreteElement.
192            if let Some(root) = self.root_element() {
193                let token = RecalcStyle::pre_traverse(root, &context);
194
195                if token.should_traverse() {
196                    // Style the elements, resolving their data
197                    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
228/// A handle to a node that Servo's style traits are implemented against
229///
230/// Since BlissNodes are not persistent (IE we don't keep the pointers around between frames), we choose to just implement
231/// the tree structure in the nodes themselves, and temporarily give out pointers during the layout phase.
232type 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            // Guard stale host ID: if the host node was removed from the tree,
274            // with() would unwrap-panic. Return self as fallback.
275            self.tree().get(host).unwrap_or(self)
276        } else {
277            // Fallback: return self if not a shadow root (shouldn't happen)
278            self
279        }
280    }
281
282    fn style_data<'b>(&self) -> Option<&'b style::stylist::CascadeData>
283    where
284        Self: 'b,
285    {
286        // Access the scoped cascade data stored on the owning BaseDocument.
287        // The doc pointer on Node is set at creation time and the BaseDocument
288        // is guaranteed to outlive all its Nodes.
289        let doc = self.doc();
290        doc.shadow_cascade_data
291            .get(&self.id)
292            .map(|boxed| &**boxed as &style::stylist::CascadeData)
293    }
294}
295
296// components/styleaapper.rs:
297impl<'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    // I think this is the same as parent_node only in the cases when the direct parent is not a real element, forcing us
331    // to travel upwards
332    //
333    // For the sake of this demo, we're just going to return the parent node ann
334    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        // This correctly uses a unique id for the OpaqueElement (unlike using a pointer to the "slot")
380        // However, it makes it impossible for us to "rehydrate" the OpaqueElement back into an actual Element
381        // which is required to implement the `implicit_scope_for_sheet_in_shadow_root` method below
382        //
383        // We should see if selectors will accept a PR that allows us to use 128bits for the OpaqueElement. Or
384        // find some other solution that will enable "rehydration". This is required to enable and use the
385        // Shadow DOM functionality in Stylo.
386        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    // These methods are implemented naively since we only threaded real nodes and not fake nodes
416    // we should try and use `find` instead of this foward/backward stuff since its ugly and slow
417    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 // self.has_namespace(ns!(html))
448    }
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                // TODO: case sensitivity
482                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                        // Represents elements with an attribute name of attr whose value can be exactly value
489                        // or can begin with value immediately followed by a hyphen, - (U+002D)
490                        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        // Handle flags that apply to the element.
579        let self_flags = flags.for_self();
580        if !self_flags.is_empty() {
581            *self.selector_flags.borrow_mut() |= self_flags;
582        }
583
584        // Handle flags that apply to the parent.
585        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            // split the class attribute
620            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        // We cannot currently implement this as we are using the NodeId as the OpaqueElement,
679        // and need a reference to the Slab to convert it back into an Element
680        //
681        // Luckily it is only needed for shadow dom.
682        None
683    }
684
685    fn traversal_children(&self) -> style::dom::LayoutIterator<Self::TraversalChildrenIterator> {
686        LayoutIterator(Traverser {
687            // dom: self.tree(),
688            parent: self,
689            child_index: 0,
690        })
691    }
692
693    fn is_html_element(&self) -> bool {
694        self.is_element()
695    }
696
697    // not implemented.....
698    fn is_mathml_element(&self) -> bool {
699        false
700    }
701
702    // need to check the namespace
703    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            // split the class attribute
738            for pheme in class_attr.split_ascii_whitespace() {
739                let atom = Atom::from(pheme); // interns the string
740                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        // Style processing not yet fully implemented
783    }
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        // Check node is a <body> element
912        let is_body_element = self.data.is_element_with_tag_name(&local_name!("body"));
913
914        // If it isn't then return early
915        if !is_body_element {
916            return false;
917        }
918
919        // If it is then check if it is a child of the root (<html>) element
920        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            // https://html.spec.whatwg.org/multipage/rendering.html#dimRendering
1016            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        // Elements with display: contents don't generate a box and cannot be containers.
1077        use style::values::specified::box_::DisplayInside;
1078        if matches!(display.inside(), DisplayInside::Contents) {
1079            return euclid::default::Size2D::new(None, None);
1080        }
1081
1082        // Check whether this element is a container (container-type != normal).
1083        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        // Return the content box size from the previous frame's layout.
1093        // Container sizes are only available after the first layout pass;
1094        // on the very first frame this returns None (container queries won't match).
1095        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        // Custom state not yet implemented
1121    }
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        // ALL_DAMAGE
1145    }
1146
1147    // fn update_animations(
1148    //     &self,
1149    //     before_change_style: Option<Arc<ComputedValues>>,
1150    //     tasks: style::context::UpdateAnimationsTasks,
1151    // ) {
1152    //     todo!()
1153    // }
1154
1155    // fn process_post_animation(&self, tasks: style::context::PostAnimationTasks) {
1156    //     todo!()
1157    // }
1158
1159    // fn needs_transitions_update(
1160    //     &self,
1161    //     before_change_style: &ComputedValues,
1162    //     after_change_style: &ComputedValues,
1163    // ) -> bool {
1164    //     todo!()
1165    // }
1166}
1167
1168pub struct Traverser<'a> {
1169    // dom: &'a Slab<Node>,
1170    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
1193/// Handle custom painters like images for layouting
1194///
1195/// todo: actually implement this
1196pub 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        // Don't process textnodees in this traversal
1228        if node.is_text_node() {
1229            return;
1230        }
1231
1232        let el = node.as_element().unwrap();
1233        // let mut data = el.mutate_data().unwrap();
1234        let mut data = unsafe { el.ensure_data() };
1235        recalc_style_at(self, traversal_data, context, el, &mut data, note_child);
1236
1237        // Gets set later on
1238        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    // use std::mem;
1259
1260    // fn assert_layout<E>() {
1261    //     assert_eq!(
1262    //         mem::size_of::<SharingCache<E>>(),
1263    //         mem::size_of::<TypelessSharingCache>()
1264    //     );
1265    //     assert_eq!(
1266    //         mem::align_of::<SharingCache<E>>(),
1267    //         mem::align_of::<TypelessSharingCache>()
1268    //     );
1269    // }
1270
1271    // let size = mem::size_of::<StyleSharingCandidate<BlissNode>>();
1272    // dbg!(size);
1273}
1274
1275#[test]
1276fn parse_inline() {
1277    // let attrs = style::attr::AttrValue::from_serialized_tokenlist(
1278    //     r#"visibility: hidden; left: 1306.5px; top: 50px; display: none;"#.to_string(),
1279    // );
1280
1281    // let val = CSSInlineStyleDeclaration();
1282}