Skip to main content

azul_core/
prop_cache.rs

1//! CSS property cache for efficient style resolution and animation.
2//!
3//! This module implements a cache layer between the raw CSS stylesheet and the rendered DOM.
4//! It resolves CSS properties for each node, handling:
5//!
6//! - **Cascade resolution**: Computes final values from CSS rules, inline styles, and inheritance
7//! - **Pseudo-class states**: Caches styles for `:hover`, `:active`, `:focus`, etc.
8//! - **Animation support**: Tracks animating properties for smooth interpolation
9//! - **Performance**: Avoids re-parsing and re-resolving unchanged properties
10//!
11//! # Architecture
12//!
13//! The cache is organized per-node and per-property-type. Each property has a dedicated
14//! getter method that:
15//!
16//! 1. Checks if the property is cached
17//! 2. If not, resolves it from CSS rules + inline styles
18//! 3. Caches the result for subsequent frames
19//!
20//! # Thread Safety
21//!
22//! Not thread-safe. Each window has its own cache instance.
23
24extern crate alloc;
25
26use alloc::{boxed::Box, collections::BTreeMap, string::String, vec::Vec};
27
28use crate::dom::NodeType;
29
30/// Tracks the origin of a CSS property value.
31/// Used to correctly implement the CSS cascade and inheritance rules.
32#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
33pub enum CssPropertyOrigin {
34    /// Property was inherited from parent node (only for inheritable properties)
35    Inherited,
36    /// Property is the node's own value (from UA CSS, CSS file, inline style, or user override)
37    Own,
38}
39
40/// A CSS property with its origin tracking.
41#[derive(Debug, Clone, PartialEq)]
42pub struct CssPropertyWithOrigin {
43    pub property: CssProperty,
44    pub origin: CssPropertyOrigin,
45}
46
47/// Represents a single step in a CSS property dependency chain.
48/// Example: "10% of node 5" or "1.2em of node 3"
49#[derive(Debug, Clone, PartialEq)]
50pub enum CssDependencyChainStep {
51    /// Value depends on a percentage of another node's resolved value
52    /// e.g., font-size: 150% means 1.5 * parent's font-size
53    Percent { source_node: NodeId, factor: f32 },
54
55    /// Value depends on an em multiple of another node's font-size
56    /// e.g., padding: 2em means 2.0 * current element's font-size
57    Em { source_node: NodeId, factor: f32 },
58
59    /// Value depends on a rem multiple of root node's font-size
60    /// e.g., margin: 1.5rem means 1.5 * root font-size
61    Rem { factor: f32 },
62
63    /// Absolute value (px, pt, etc.) - no further dependencies
64    Absolute { pixels: f32 },
65}
66
67/// A dependency chain for a CSS property value.
68/// Example: [10% of node 10, then 1.2em of that, then 1.5em of that]
69///
70/// During layout, this chain is resolved by:
71/// 1. Starting with the root dependency (e.g., node 10's resolved font-size)
72/// 2. Applying each transformation in sequence
73/// 3. Producing the final pixel value
74#[derive(Debug, Clone, PartialEq)]
75pub struct CssDependencyChain {
76    /// The property type this chain is for
77    pub property_type: CssPropertyType,
78
79    /// The ordered list of dependencies, from root to leaf
80    /// Empty if the value is absolute (no dependencies)
81    pub steps: Vec<CssDependencyChainStep>,
82
83    /// Cached resolved value (in pixels) from the last resolution
84    /// None if the chain hasn't been resolved yet
85    pub cached_pixels: Option<f32>,
86}
87
88impl CssDependencyChain {
89    /// Create a new dependency chain for an absolute pixel value
90    pub fn absolute(property_type: CssPropertyType, pixels: f32) -> Self {
91        Self {
92            property_type,
93            steps: vec![CssDependencyChainStep::Absolute { pixels }],
94            cached_pixels: Some(pixels),
95        }
96    }
97
98    /// Create a new dependency chain for a percentage-based value
99    pub fn percent(property_type: CssPropertyType, source_node: NodeId, factor: f32) -> Self {
100        Self {
101            property_type,
102            steps: vec![CssDependencyChainStep::Percent {
103                source_node,
104                factor,
105            }],
106            cached_pixels: None,
107        }
108    }
109
110    /// Create a new dependency chain for an em-based value
111    pub fn em(property_type: CssPropertyType, source_node: NodeId, factor: f32) -> Self {
112        Self {
113            property_type,
114            steps: vec![CssDependencyChainStep::Em {
115                source_node,
116                factor,
117            }],
118            cached_pixels: None,
119        }
120    }
121
122    /// Create a new dependency chain for a rem-based value
123    pub fn rem(property_type: CssPropertyType, factor: f32) -> Self {
124        Self {
125            property_type,
126            steps: vec![CssDependencyChainStep::Rem { factor }],
127            cached_pixels: None,
128        }
129    }
130
131    /// Check if this chain depends on a specific node
132    pub fn depends_on(&self, node_id: NodeId) -> bool {
133        self.steps.iter().any(|step| match step {
134            CssDependencyChainStep::Percent { source_node, .. } => *source_node == node_id,
135            CssDependencyChainStep::Em { source_node, .. } => *source_node == node_id,
136            _ => false,
137        })
138    }
139
140    /// Resolve the dependency chain to a pixel value.
141    ///
142    /// # Arguments
143    /// * `resolve_node_value` - Closure to resolve a node's property value to pixels
144    /// * `root_font_size` - Root element's font-size for rem calculations
145    ///
146    /// # Returns
147    /// The resolved pixel value, or None if any dependency couldn't be resolved
148    pub fn resolve<F>(&mut self, mut resolve_node_value: F, root_font_size: f32) -> Option<f32>
149    where
150        F: FnMut(NodeId, CssPropertyType) -> Option<f32>,
151    {
152        let mut current_value: Option<f32> = None;
153
154        for step in &self.steps {
155            match step {
156                CssDependencyChainStep::Absolute { pixels } => {
157                    current_value = Some(*pixels);
158                }
159                CssDependencyChainStep::Percent {
160                    source_node,
161                    factor,
162                } => {
163                    let source_val = resolve_node_value(*source_node, self.property_type)?;
164                    current_value = Some(source_val * factor);
165                }
166                CssDependencyChainStep::Em {
167                    source_node,
168                    factor,
169                } => {
170                    let font_size = resolve_node_value(*source_node, CssPropertyType::FontSize)?;
171                    current_value = Some(font_size * factor);
172                }
173                CssDependencyChainStep::Rem { factor } => {
174                    current_value = Some(root_font_size * factor);
175                }
176            }
177        }
178
179        self.cached_pixels = current_value;
180        current_value
181    }
182}
183
184use azul_css::{
185    css::{Css, CssPath},
186    props::{
187        basic::{StyleFontFamily, StyleFontFamilyVec, StyleFontSize},
188        layout::{LayoutDisplay, LayoutHeight, LayoutWidth},
189        property::{
190            BoxDecorationBreakValue, BreakInsideValue, CaretAnimationDurationValue,
191            CaretColorValue, CaretWidthValue, ClipPathValue, ColumnCountValue, ColumnFillValue,
192            ColumnRuleColorValue, ColumnRuleStyleValue, ColumnRuleWidthValue, ColumnSpanValue,
193            ColumnWidthValue, ContentValue, CounterIncrementValue, CounterResetValue, CssProperty,
194            CssPropertyType, FlowFromValue, FlowIntoValue, LayoutAlignContentValue,
195            LayoutAlignItemsValue, LayoutAlignSelfValue, LayoutBorderBottomWidthValue,
196            LayoutBorderLeftWidthValue, LayoutBorderRightWidthValue, LayoutBorderSpacingValue,
197            LayoutBorderTopWidthValue, LayoutBoxSizingValue, LayoutClearValue,
198            LayoutColumnGapValue, LayoutDisplayValue, LayoutFlexBasisValue,
199            LayoutFlexDirectionValue, LayoutFlexGrowValue, LayoutFlexShrinkValue,
200            LayoutFlexWrapValue, LayoutFloatValue, LayoutGapValue, LayoutGridAutoColumnsValue,
201            LayoutGridAutoFlowValue, LayoutGridAutoRowsValue, LayoutGridColumnValue,
202            LayoutGridRowValue, LayoutGridTemplateColumnsValue, LayoutGridTemplateRowsValue,
203            LayoutHeightValue, LayoutInsetBottomValue, LayoutJustifyContentValue,
204            LayoutJustifyItemsValue, LayoutJustifySelfValue, LayoutLeftValue,
205            LayoutMarginBottomValue, LayoutMarginLeftValue, LayoutMarginRightValue,
206            LayoutMarginTopValue, LayoutMaxHeightValue, LayoutMaxWidthValue, LayoutMinHeightValue,
207            LayoutMinWidthValue, LayoutOverflowValue, LayoutPaddingBottomValue,
208            LayoutPaddingLeftValue, LayoutPaddingRightValue, LayoutPaddingTopValue,
209            LayoutPositionValue, LayoutRightValue, LayoutRowGapValue, LayoutScrollbarWidthValue,
210            LayoutTableLayoutValue, LayoutTextJustifyValue, LayoutTopValue, LayoutWidthValue,
211            LayoutWritingModeValue, LayoutZIndexValue, OrphansValue, PageBreakValue,
212            ScrollbarStyleValue, SelectionBackgroundColorValue, SelectionColorValue,
213            SelectionRadiusValue, ShapeImageThresholdValue, ShapeInsideValue, ShapeMarginValue,
214            ShapeOutsideValue, StringSetValue, StyleBackfaceVisibilityValue,
215            StyleBackgroundContentVecValue, StyleBackgroundPositionVecValue,
216            StyleBackgroundRepeatVecValue, StyleBackgroundSizeVecValue,
217            StyleBorderBottomColorValue, StyleBorderBottomLeftRadiusValue,
218            StyleBorderBottomRightRadiusValue, StyleBorderBottomStyleValue,
219            StyleBorderCollapseValue, StyleBorderLeftColorValue, StyleBorderLeftStyleValue,
220            StyleBorderRightColorValue, StyleBorderRightStyleValue, StyleBorderTopColorValue,
221            StyleBorderTopLeftRadiusValue, StyleBorderTopRightRadiusValue,
222            StyleBorderTopStyleValue, StyleBoxShadowValue, StyleCaptionSideValue, StyleCursorValue,
223            StyleDirectionValue, StyleEmptyCellsValue, StyleExclusionMarginValue,
224            StyleFilterVecValue, StyleFontFamilyVecValue, StyleFontSizeValue, StyleFontStyleValue,
225            StyleFontValue, StyleFontWeightValue, StyleHangingPunctuationValue,
226            StyleHyphenationLanguageValue, StyleHyphensValue, StyleInitialLetterValue,
227            StyleLetterSpacingValue, StyleLineClampValue, StyleLineHeightValue,
228            StyleListStylePositionValue, StyleListStyleTypeValue, StyleMixBlendModeValue,
229            StyleOpacityValue, StylePerspectiveOriginValue, StyleScrollbarColorValue,
230            StyleTabWidthValue, StyleTextAlignValue, StyleTextColorValue,
231            StyleTextCombineUprightValue, StyleTextDecorationValue, StyleTextIndentValue,
232            StyleTransformOriginValue, StyleTransformVecValue, StyleUserSelectValue,
233            StyleVerticalAlignValue, StyleVisibilityValue, StyleWhiteSpaceValue,
234            StyleWordSpacingValue, WidowsValue,
235        },
236        style::{StyleCursor, StyleTextColor, StyleTransformOrigin},
237    },
238    AzString,
239};
240
241use crate::{
242    dom::{NodeData, NodeId, TabIndex, TagId},
243    id::{NodeDataContainer, NodeDataContainerRef},
244    style::CascadeInfo,
245    styled_dom::{
246        NodeHierarchyItem, NodeHierarchyItemId, NodeHierarchyItemVec, ParentWithNodeDepth,
247        ParentWithNodeDepthVec, StyledNodeState, TagIdToNodeIdMapping,
248    },
249};
250
251use azul_css::dynamic_selector::{
252    CssPropertyWithConditions, CssPropertyWithConditionsVec, DynamicSelectorContext,
253};
254
255/// Macro to match on any CssProperty variant and access the inner CssPropertyValue<T>.
256/// This allows generic operations on cascade keywords without writing 190+ match arms.
257macro_rules! match_property_value {
258    ($property:expr, $value:ident, $expr:expr) => {
259        match $property {
260            CssProperty::CaretColor($value) => $expr,
261            CssProperty::CaretAnimationDuration($value) => $expr,
262            CssProperty::SelectionBackgroundColor($value) => $expr,
263            CssProperty::SelectionColor($value) => $expr,
264            CssProperty::SelectionRadius($value) => $expr,
265            CssProperty::TextColor($value) => $expr,
266            CssProperty::FontSize($value) => $expr,
267            CssProperty::FontFamily($value) => $expr,
268            CssProperty::FontWeight($value) => $expr,
269            CssProperty::FontStyle($value) => $expr,
270            CssProperty::TextAlign($value) => $expr,
271            CssProperty::TextJustify($value) => $expr,
272            CssProperty::VerticalAlign($value) => $expr,
273            CssProperty::LetterSpacing($value) => $expr,
274            CssProperty::TextIndent($value) => $expr,
275            CssProperty::InitialLetter($value) => $expr,
276            CssProperty::LineClamp($value) => $expr,
277            CssProperty::HangingPunctuation($value) => $expr,
278            CssProperty::TextCombineUpright($value) => $expr,
279            CssProperty::ExclusionMargin($value) => $expr,
280            CssProperty::HyphenationLanguage($value) => $expr,
281            CssProperty::LineHeight($value) => $expr,
282            CssProperty::WordSpacing($value) => $expr,
283            CssProperty::TabWidth($value) => $expr,
284            CssProperty::WhiteSpace($value) => $expr,
285            CssProperty::Hyphens($value) => $expr,
286            CssProperty::Direction($value) => $expr,
287            CssProperty::UserSelect($value) => $expr,
288            CssProperty::TextDecoration($value) => $expr,
289            CssProperty::Cursor($value) => $expr,
290            CssProperty::Display($value) => $expr,
291            CssProperty::Float($value) => $expr,
292            CssProperty::BoxSizing($value) => $expr,
293            CssProperty::Width($value) => $expr,
294            CssProperty::Height($value) => $expr,
295            CssProperty::MinWidth($value) => $expr,
296            CssProperty::MinHeight($value) => $expr,
297            CssProperty::MaxWidth($value) => $expr,
298            CssProperty::MaxHeight($value) => $expr,
299            CssProperty::Position($value) => $expr,
300            CssProperty::Top($value) => $expr,
301            CssProperty::Right($value) => $expr,
302            CssProperty::Left($value) => $expr,
303            CssProperty::Bottom($value) => $expr,
304            CssProperty::ZIndex($value) => $expr,
305            CssProperty::FlexWrap($value) => $expr,
306            CssProperty::FlexDirection($value) => $expr,
307            CssProperty::FlexGrow($value) => $expr,
308            CssProperty::FlexShrink($value) => $expr,
309            CssProperty::FlexBasis($value) => $expr,
310            CssProperty::JustifyContent($value) => $expr,
311            CssProperty::AlignItems($value) => $expr,
312            CssProperty::AlignContent($value) => $expr,
313            CssProperty::AlignSelf($value) => $expr,
314            CssProperty::JustifyItems($value) => $expr,
315            CssProperty::JustifySelf($value) => $expr,
316            CssProperty::BackgroundContent($value) => $expr,
317            CssProperty::BackgroundPosition($value) => $expr,
318            CssProperty::BackgroundSize($value) => $expr,
319            CssProperty::BackgroundRepeat($value) => $expr,
320            CssProperty::OverflowX($value) => $expr,
321            CssProperty::OverflowY($value) => $expr,
322            CssProperty::PaddingTop($value) => $expr,
323            CssProperty::PaddingLeft($value) => $expr,
324            CssProperty::PaddingRight($value) => $expr,
325            CssProperty::PaddingBottom($value) => $expr,
326            CssProperty::MarginTop($value) => $expr,
327            CssProperty::MarginLeft($value) => $expr,
328            CssProperty::MarginRight($value) => $expr,
329            CssProperty::MarginBottom($value) => $expr,
330            CssProperty::BorderTopLeftRadius($value) => $expr,
331            CssProperty::BorderTopRightRadius($value) => $expr,
332            CssProperty::BorderBottomLeftRadius($value) => $expr,
333            CssProperty::BorderBottomRightRadius($value) => $expr,
334            CssProperty::BorderTopColor($value) => $expr,
335            CssProperty::BorderRightColor($value) => $expr,
336            CssProperty::BorderLeftColor($value) => $expr,
337            CssProperty::BorderBottomColor($value) => $expr,
338            CssProperty::BorderTopStyle($value) => $expr,
339            CssProperty::BorderRightStyle($value) => $expr,
340            CssProperty::BorderLeftStyle($value) => $expr,
341            CssProperty::BorderBottomStyle($value) => $expr,
342            CssProperty::BorderTopWidth($value) => $expr,
343            CssProperty::BorderRightWidth($value) => $expr,
344            CssProperty::BorderLeftWidth($value) => $expr,
345            CssProperty::BorderBottomWidth($value) => $expr,
346            CssProperty::BoxShadow($value) => $expr,
347            CssProperty::Opacity($value) => $expr,
348            CssProperty::Transform($value) => $expr,
349            CssProperty::TransformOrigin($value) => $expr,
350            CssProperty::PerspectiveOrigin($value) => $expr,
351            CssProperty::BackfaceVisibility($value) => $expr,
352            CssProperty::MixBlendMode($value) => $expr,
353            CssProperty::Filter($value) => $expr,
354            CssProperty::Visibility($value) => $expr,
355            CssProperty::WritingMode($value) => $expr,
356            CssProperty::GridTemplateColumns($value) => $expr,
357            CssProperty::GridTemplateRows($value) => $expr,
358            CssProperty::GridAutoColumns($value) => $expr,
359            CssProperty::GridAutoRows($value) => $expr,
360            CssProperty::GridAutoFlow($value) => $expr,
361            CssProperty::GridColumn($value) => $expr,
362            CssProperty::GridRow($value) => $expr,
363            CssProperty::Gap($value) => $expr,
364            CssProperty::ColumnGap($value) => $expr,
365            CssProperty::RowGap($value) => $expr,
366            CssProperty::Clear($value) => $expr,
367            CssProperty::ScrollbarStyle($value) => $expr,
368            CssProperty::ScrollbarWidth($value) => $expr,
369            CssProperty::ScrollbarColor($value) => $expr,
370            CssProperty::ListStyleType($value) => $expr,
371            CssProperty::ListStylePosition($value) => $expr,
372            CssProperty::Font($value) => $expr,
373            CssProperty::ColumnCount($value) => $expr,
374            CssProperty::ColumnWidth($value) => $expr,
375            CssProperty::ColumnSpan($value) => $expr,
376            CssProperty::ColumnFill($value) => $expr,
377            CssProperty::ColumnRuleStyle($value) => $expr,
378            CssProperty::ColumnRuleWidth($value) => $expr,
379            CssProperty::ColumnRuleColor($value) => $expr,
380            CssProperty::FlowInto($value) => $expr,
381            CssProperty::FlowFrom($value) => $expr,
382            CssProperty::ShapeOutside($value) => $expr,
383            CssProperty::ShapeInside($value) => $expr,
384            CssProperty::ShapeImageThreshold($value) => $expr,
385            CssProperty::ShapeMargin($value) => $expr,
386            CssProperty::ClipPath($value) => $expr,
387            CssProperty::Content($value) => $expr,
388            CssProperty::CounterIncrement($value) => $expr,
389            CssProperty::CounterReset($value) => $expr,
390            CssProperty::StringSet($value) => $expr,
391            CssProperty::Orphans($value) => $expr,
392            CssProperty::Widows($value) => $expr,
393            CssProperty::PageBreakBefore($value) => $expr,
394            CssProperty::PageBreakAfter($value) => $expr,
395            CssProperty::PageBreakInside($value) => $expr,
396            CssProperty::BreakInside($value) => $expr,
397            CssProperty::BoxDecorationBreak($value) => $expr,
398            CssProperty::TableLayout($value) => $expr,
399            CssProperty::BorderCollapse($value) => $expr,
400            CssProperty::BorderSpacing($value) => $expr,
401            CssProperty::CaptionSide($value) => $expr,
402            CssProperty::EmptyCells($value) => $expr,
403        }
404    };
405}
406
407/// Returns the CSS-specified initial value for a given property type.
408/// These are the default values defined by the CSS specification, not UA stylesheet values.
409fn get_initial_value(property_type: CssPropertyType) -> Option<CssProperty> {
410    use azul_css::css::CssPropertyValue;
411
412    // For now, we return None for most properties and implement only the most critical ones.
413    // This can be expanded as needed.
414    match property_type {
415        // Most properties: return None (no initial value implemented yet)
416        // This means cascade keywords will fall back to parent values or remain unresolved
417        _ => None,
418    }
419}
420
421// NOTE: To avoid large memory allocations, this is a "cache" that stores all the CSS properties
422// found in the DOM. This cache exists on a per-DOM basis, so it scales independent of how many
423// nodes are in the DOM.
424//
425// If each node would carry its own CSS properties, that would unnecessarily consume memory
426// because most nodes use the default properties or override only one or two properties.
427//
428// The cache can compute the property of any node at any given time, given the current node
429// state (hover, active, focused, normal). This way we don't have to duplicate the CSS properties
430// onto every single node and exchange them when the style changes. Two caches can be appended
431// to each other by simply merging their NodeIds.
432#[derive(Debug, Default, Clone, PartialEq)]
433pub struct CssPropertyCache {
434    // number of nodes in the current DOM
435    pub node_count: usize,
436
437    // properties that were overridden in callbacks (not specific to any node state)
438    pub user_overridden_properties: BTreeMap<NodeId, BTreeMap<CssPropertyType, CssProperty>>,
439
440    // non-default CSS properties that were cascaded from the parent
441    pub cascaded_normal_props: BTreeMap<NodeId, BTreeMap<CssPropertyType, CssProperty>>,
442    pub cascaded_hover_props: BTreeMap<NodeId, BTreeMap<CssPropertyType, CssProperty>>,
443    pub cascaded_active_props: BTreeMap<NodeId, BTreeMap<CssPropertyType, CssProperty>>,
444    pub cascaded_focus_props: BTreeMap<NodeId, BTreeMap<CssPropertyType, CssProperty>>,
445
446    // non-default CSS properties that were set via a CSS file
447    pub css_normal_props: BTreeMap<NodeId, BTreeMap<CssPropertyType, CssProperty>>,
448    pub css_hover_props: BTreeMap<NodeId, BTreeMap<CssPropertyType, CssProperty>>,
449    pub css_active_props: BTreeMap<NodeId, BTreeMap<CssPropertyType, CssProperty>>,
450    pub css_focus_props: BTreeMap<NodeId, BTreeMap<CssPropertyType, CssProperty>>,
451
452    // NEW: Computed values cache - pre-resolved inherited properties
453    // This cache contains the final computed values after inheritance resolution.
454    // Updated whenever a property changes or the DOM structure changes.
455    // Properties are stored in contiguous memory per node for efficient access.
456    // Each property is tagged with its origin (Inherited vs Own) to correctly handle
457    // the CSS cascade when properties are updated.
458    pub computed_values: BTreeMap<NodeId, BTreeMap<CssPropertyType, CssPropertyWithOrigin>>,
459
460    // NEW: Dependency chains for relative values (em, %, rem, etc.)
461    // Maps NodeId → PropertyType → DependencyChain
462    // This allows efficient updates when a property changes:
463    // 1. Find all chains that depend on the changed node
464    // 2. Invalidate their cached values
465    // 3. Resolve chains during layout when needed
466    //
467    // Example: If node 5's font-size changes from 16px to 20px:
468    // - All child nodes with font-size: 1.5em need recalculation
469    // - All nodes with padding: 2em that depend on node 5 need updates
470    pub dependency_chains: BTreeMap<NodeId, BTreeMap<CssPropertyType, CssDependencyChain>>,
471}
472
473impl CssPropertyCache {
474    /// Restyles the CSS property cache with a new CSS file
475    #[must_use]
476    pub fn restyle(
477        &mut self,
478        css: &mut Css,
479        node_data: &NodeDataContainerRef<NodeData>,
480        node_hierarchy: &NodeHierarchyItemVec,
481        non_leaf_nodes: &ParentWithNodeDepthVec,
482        html_tree: &NodeDataContainerRef<CascadeInfo>,
483    ) -> Vec<TagIdToNodeIdMapping> {
484        use azul_css::{
485            css::{CssDeclaration, CssPathPseudoSelector::*},
486            props::layout::LayoutDisplay,
487        };
488
489        let css_is_empty = css.is_empty();
490
491        if !css_is_empty {
492            css.sort_by_specificity();
493
494            macro_rules! filter_rules {($expected_pseudo_selector:expr, $node_id:expr) => {{
495                css
496                .rules() // can not be parallelized due to specificity order matching
497                .filter(|rule_block| crate::style::rule_ends_with(&rule_block.path, $expected_pseudo_selector))
498                .filter(|rule_block| crate::style::matches_html_element(
499                    &rule_block.path,
500                    $node_id,
501                    &node_hierarchy.as_container(),
502                    &node_data,
503                    &html_tree,
504                    $expected_pseudo_selector
505                ))
506                // rule matched, now copy all the styles of this rule
507                .flat_map(|matched_rule| {
508                    matched_rule.declarations
509                    .iter()
510                    .filter_map(move |declaration| {
511                        match declaration {
512                            CssDeclaration::Static(s) => Some(s),
513                            CssDeclaration::Dynamic(_d) => None, // TODO: No variable support yet!
514                        }
515                    })
516                })
517                .map(|prop| prop.clone())
518                .collect::<Vec<CssProperty>>()
519            }};}
520
521            // NOTE: This is wrong, but fast
522            //
523            // Get all nodes that end with `:hover`, `:focus` or `:active`
524            // and copy the respective styles to the `hover_css_constraints`, etc. respectively
525            //
526            // NOTE: This won't work correctly for paths with `.blah:hover > #thing`
527            // but that can be fixed later
528
529            // go through each HTML node (in parallel) and see which CSS rules match
530            let css_normal_rules: NodeDataContainer<(NodeId, Vec<CssProperty>)> = node_data
531                .transform_nodeid_multithreaded_optional(|node_id| {
532                    let r = filter_rules!(None, node_id);
533                    if r.is_empty() {
534                        None
535                    } else {
536                        Some((node_id, r))
537                    }
538                });
539
540            let css_hover_rules: NodeDataContainer<(NodeId, Vec<CssProperty>)> = node_data
541                .transform_nodeid_multithreaded_optional(|node_id| {
542                    let r = filter_rules!(Some(Hover), node_id);
543                    if r.is_empty() {
544                        None
545                    } else {
546                        Some((node_id, r))
547                    }
548                });
549
550            let css_active_rules: NodeDataContainer<(NodeId, Vec<CssProperty>)> = node_data
551                .transform_nodeid_multithreaded_optional(|node_id| {
552                    let r = filter_rules!(Some(Active), node_id);
553                    if r.is_empty() {
554                        None
555                    } else {
556                        Some((node_id, r))
557                    }
558                });
559
560            let css_focus_rules: NodeDataContainer<(NodeId, Vec<CssProperty>)> = node_data
561                .transform_nodeid_multithreaded_optional(|node_id| {
562                    let r = filter_rules!(Some(Focus), node_id);
563                    if r.is_empty() {
564                        None
565                    } else {
566                        Some((node_id, r))
567                    }
568                });
569
570            self.css_normal_props = css_normal_rules
571                .internal
572                .into_iter()
573                .map(|(n, map)| {
574                    (
575                        n,
576                        map.into_iter()
577                            .map(|prop| (prop.get_type(), prop))
578                            .collect(),
579                    )
580                })
581                .collect();
582
583            self.css_hover_props = css_hover_rules
584                .internal
585                .into_iter()
586                .map(|(n, map)| {
587                    (
588                        n,
589                        map.into_iter()
590                            .map(|prop| (prop.get_type(), prop))
591                            .collect(),
592                    )
593                })
594                .collect();
595
596            self.css_active_props = css_active_rules
597                .internal
598                .into_iter()
599                .map(|(n, map)| {
600                    (
601                        n,
602                        map.into_iter()
603                            .map(|prop| (prop.get_type(), prop))
604                            .collect(),
605                    )
606                })
607                .collect();
608
609            self.css_focus_props = css_focus_rules
610                .internal
611                .into_iter()
612                .map(|(n, map)| {
613                    (
614                        n,
615                        map.into_iter()
616                            .map(|prop| (prop.get_type(), prop))
617                            .collect(),
618                    )
619                })
620                .collect();
621        }
622
623        // Inheritance: Inherit all values of the parent to the children, but
624        // only if the property is inheritable and isn't yet set
625        for ParentWithNodeDepth { depth: _, node_id } in non_leaf_nodes.iter() {
626            let parent_id = match node_id.into_crate_internal() {
627                Some(s) => s,
628                None => continue,
629            };
630
631            // Inherit CSS properties from map A -> map B
632            // map B will be populated with all inherited CSS properties
633            macro_rules! inherit_props {
634                ($from_inherit_map:expr, $to_inherit_map:expr) => {
635                    let parent_inheritable_css_props =
636                        $from_inherit_map.get(&parent_id).and_then(|map| {
637                            let parent_inherit_props = map
638                                .iter()
639                                .filter(|(css_prop_type, _)| css_prop_type.is_inheritable())
640                                .map(|(css_prop_type, css_prop)| (*css_prop_type, css_prop.clone()))
641                                .collect::<Vec<(CssPropertyType, CssProperty)>>();
642                            if parent_inherit_props.is_empty() {
643                                None
644                            } else {
645                                Some(parent_inherit_props)
646                            }
647                        });
648
649                    match parent_inheritable_css_props {
650                        Some(pi) => {
651                            // only override the rule if the child does not already have an
652                            // inherited rule
653                            for child_id in parent_id.az_children(&node_hierarchy.as_container()) {
654                                let child_map = $to_inherit_map
655                                    .entry(child_id)
656                                    .or_insert_with(|| BTreeMap::new());
657
658                                for (inherited_rule_type, inherited_rule_value) in pi.iter() {
659                                    let _ = child_map
660                                        .entry(*inherited_rule_type)
661                                        .or_insert_with(|| inherited_rule_value.clone());
662                                }
663                            }
664                        }
665                        None => {}
666                    }
667                };
668            }
669
670            // Same as inherit_props, but filters along the inline node data instead
671            // Uses the new CssPropertyWithConditions system
672            macro_rules! inherit_inline_css_props {($filter_pseudo_state:expr, $to_inherit_map:expr) => {{
673                let parent_inheritable_css_props = &node_data[parent_id]
674                .css_props
675                .iter()
676                // Filter by pseudo state condition
677                .filter(|css_prop| {
678                    // Check if property matches the desired pseudo state
679                    let conditions = css_prop.apply_if.as_slice();
680                    if conditions.is_empty() {
681                        // No conditions = Normal state
682                        $filter_pseudo_state == PseudoStateType::Normal
683                    } else {
684                        // Check if all conditions are the matching pseudo state
685                        conditions.iter().all(|c| {
686                            matches!(c, DynamicSelector::PseudoState(state) if *state == $filter_pseudo_state)
687                        })
688                    }
689                })
690                // Get the inner property
691                .map(|css_prop| &css_prop.property)
692                // test whether the property is inheritable
693                .filter(|css_prop| css_prop.get_type().is_inheritable())
694                .cloned()
695                .collect::<Vec<CssProperty>>();
696
697                if !parent_inheritable_css_props.is_empty() {
698                    // only override the rule if the child does not already have an inherited rule
699                    for child_id in parent_id.az_children(&node_hierarchy.as_container()) {
700                        let child_map = $to_inherit_map.entry(child_id).or_insert_with(|| BTreeMap::new());
701                        for inherited_rule in parent_inheritable_css_props.iter() {
702                            let _ = child_map
703                            .entry(inherited_rule.get_type())
704                            .or_insert_with(|| inherited_rule.clone());
705                        }
706                    }
707                }
708
709            }};}
710
711            use azul_css::dynamic_selector::{DynamicSelector, PseudoStateType};
712            // strongest inheritance first
713
714            // Inherit inline CSS properties
715            inherit_inline_css_props!(PseudoStateType::Normal, self.cascaded_normal_props);
716            inherit_inline_css_props!(PseudoStateType::Hover, self.cascaded_hover_props);
717            inherit_inline_css_props!(PseudoStateType::Active, self.cascaded_active_props);
718            inherit_inline_css_props!(PseudoStateType::Focus, self.cascaded_focus_props);
719
720            // Inherit the CSS properties from the CSS file
721            if !css_is_empty {
722                inherit_props!(self.css_normal_props, self.cascaded_normal_props);
723                inherit_props!(self.css_hover_props, self.cascaded_hover_props);
724                inherit_props!(self.css_active_props, self.cascaded_active_props);
725                inherit_props!(self.css_focus_props, self.cascaded_focus_props);
726            }
727
728            // Inherit properties that were inherited in a previous iteration of the loop
729            inherit_props!(self.cascaded_normal_props, self.cascaded_normal_props);
730            inherit_props!(self.cascaded_hover_props, self.cascaded_hover_props);
731            inherit_props!(self.cascaded_active_props, self.cascaded_active_props);
732            inherit_props!(self.cascaded_focus_props, self.cascaded_focus_props);
733        }
734
735        // When restyling, the tag / node ID mappings may change, regenerate them
736        // See if the node should have a hit-testing tag ID
737        let default_node_state = StyledNodeState::default();
738
739        // In order to hit-test `:hover` and `:active` selectors,
740        // we need to insert "tag IDs" for all rectangles
741        // that have a non-normal path ending, for example if we have
742        // `#thing:hover`, then all nodes selected by `#thing`
743        // need to get a TagId, otherwise, they can't be hit-tested.
744
745        // NOTE: restyling a DOM may change the :hover nodes, which is
746        // why the tag IDs have to be re-generated on every .restyle() call!
747        
748        // Keep a reference to the node data container for use in the closure
749        let node_data_container = &node_data.internal;
750        
751        node_data
752            .internal
753            .iter()
754            .enumerate()
755            .filter_map(|(node_id, node_data)| {
756                let node_id = NodeId::new(node_id);
757
758                let should_auto_insert_tabindex = node_data
759                    .get_callbacks()
760                    .iter()
761                    .any(|cb| cb.event.is_focus_callback());
762
763                let tab_index = match node_data.get_tab_index() {
764                    Some(s) => Some(*s),
765                    None => {
766                        if should_auto_insert_tabindex {
767                            Some(TabIndex::Auto)
768                        } else {
769                            None
770                        }
771                    }
772                };
773
774                let mut node_should_have_tag = false;
775
776                // workaround for "goto end" - early break if
777                // one of the conditions is true
778                loop {
779                    // check for display: none
780                    let display = self
781                        .get_display(&node_data, &node_id, &default_node_state)
782                        .and_then(|p| p.get_property_or_default())
783                        .unwrap_or_default();
784
785                    if display == LayoutDisplay::None {
786                        node_should_have_tag = false;
787                        break;
788                    }
789
790                    if node_data.has_context_menu() {
791                        node_should_have_tag = true;
792                        break;
793                    }
794
795                    if tab_index.is_some() {
796                        node_should_have_tag = true;
797                        break;
798                    }
799
800                    // check for context menu
801                    if node_data.get_context_menu().is_some() {
802                        node_should_have_tag = true;
803                        break;
804                    }
805
806                    // check for :hover
807                    let node_has_hover_props = {
808                        use azul_css::dynamic_selector::{DynamicSelector, PseudoStateType};
809                        node_data.css_props.as_ref().iter().any(|p| {
810                            p.apply_if.as_slice().iter().any(|c| {
811                                matches!(c, DynamicSelector::PseudoState(PseudoStateType::Hover))
812                            })
813                        })
814                    } || self.css_hover_props.get(&node_id).is_some()
815                        || self.cascaded_hover_props.get(&node_id).is_some();
816
817                    if node_has_hover_props {
818                        node_should_have_tag = true;
819                        break;
820                    }
821
822                    // check for :active
823                    let node_has_active_props = {
824                        use azul_css::dynamic_selector::{DynamicSelector, PseudoStateType};
825                        node_data.css_props.as_ref().iter().any(|p| {
826                            p.apply_if.as_slice().iter().any(|c| {
827                                matches!(c, DynamicSelector::PseudoState(PseudoStateType::Active))
828                            })
829                        })
830                    } || self.css_active_props.get(&node_id).is_some()
831                        || self.cascaded_active_props.get(&node_id).is_some();
832
833                    if node_has_active_props {
834                        node_should_have_tag = true;
835                        break;
836                    }
837
838                    // check for :focus
839                    let node_has_focus_props = {
840                        use azul_css::dynamic_selector::{DynamicSelector, PseudoStateType};
841                        node_data.css_props.as_ref().iter().any(|p| {
842                            p.apply_if.as_slice().iter().any(|c| {
843                                matches!(c, DynamicSelector::PseudoState(PseudoStateType::Focus))
844                            })
845                        })
846                    } || self.css_focus_props.get(&node_id).is_some()
847                        || self.cascaded_focus_props.get(&node_id).is_some();
848
849                    if node_has_focus_props {
850                        node_should_have_tag = true;
851                        break;
852                    }
853
854                    // check whether any Hover(), Active() or Focus() callbacks are present
855                    let node_only_window_callbacks = node_data.get_callbacks().is_empty()
856                        || node_data
857                            .get_callbacks()
858                            .iter()
859                            .all(|cb| cb.event.is_window_callback());
860
861                    if !node_only_window_callbacks {
862                        node_should_have_tag = true;
863                        break;
864                    }
865
866                    // check for non-default cursor: property - needed for hit-testing cursor
867                    let node_has_non_default_cursor = self
868                        .get_cursor(&node_data, &node_id, &default_node_state)
869                        .is_some();
870
871                    if node_has_non_default_cursor {
872                        node_should_have_tag = true;
873                        break;
874                    }
875
876                    // check for overflow: scroll or overflow: auto - needed for scroll hit-testing
877                    // Nodes with these overflow values need hit-test tags so that
878                    // scroll wheel events can be correctly assigned to scrollable containers
879                    let node_has_overflow_scroll = {
880                        use azul_css::props::layout::LayoutOverflow;
881                        let overflow_x = self
882                            .get_overflow_x(&node_data, &node_id, &default_node_state)
883                            .and_then(|p| p.get_property_or_default());
884                        let overflow_y = self
885                            .get_overflow_y(&node_data, &node_id, &default_node_state)
886                            .and_then(|p| p.get_property_or_default());
887
888                        let x_scrollable = matches!(
889                            overflow_x,
890                            Some(LayoutOverflow::Scroll | LayoutOverflow::Auto)
891                        );
892                        let y_scrollable = matches!(
893                            overflow_y,
894                            Some(LayoutOverflow::Scroll | LayoutOverflow::Auto)
895                        );
896                        x_scrollable || y_scrollable
897                    };
898
899                    if node_has_overflow_scroll {
900                        node_should_have_tag = true;
901                        break;
902                    }
903
904                    // Check for selectable text - nodes that contain text children and
905                    // user-select != none need hit-test tags for text selection support.
906                    // The cursor resolution algorithm in CursorTypeHitTest ensures that
907                    // explicit cursor properties (e.g., cursor:pointer on button) take
908                    // precedence over the implicit I-beam from text children.
909                    let node_has_selectable_text = {
910                        use azul_css::props::style::StyleUserSelect;
911                        use crate::dom::NodeType;
912                        
913                        // Check if this node has immediate text children
914                        let has_text_children = {
915                            let hier = node_hierarchy.as_container()[node_id];
916                            let mut has_text = false;
917                            if let Some(first_child) = hier.first_child_id(node_id) {
918                                let mut child_id = Some(first_child);
919                                while let Some(cid) = child_id {
920                                    let child_data = &node_data_container[cid.index()];
921                                    if matches!(child_data.get_node_type(), NodeType::Text(_)) {
922                                        has_text = true;
923                                        break;
924                                    }
925                                    child_id = node_hierarchy.as_container()[cid].next_sibling_id();
926                                }
927                            }
928                            has_text
929                        };
930                        
931                        if has_text_children {
932                            // Check user-select property on this container (default is selectable)
933                            let user_select = self
934                                .get_user_select(&node_data, &node_id, &default_node_state)
935                                .and_then(|p| p.get_property().cloned())
936                                .unwrap_or(StyleUserSelect::Auto);
937                            
938                            !matches!(user_select, StyleUserSelect::None)
939                        } else {
940                            false
941                        }
942                    };
943
944                    if node_has_selectable_text {
945                        node_should_have_tag = true;
946                        break;
947                    }
948
949                    break;
950                }
951
952                if !node_should_have_tag {
953                    None
954                } else {
955                    Some(TagIdToNodeIdMapping {
956                        tag_id: TagId::from_crate_internal(TagId::unique()),
957                        node_id: NodeHierarchyItemId::from_crate_internal(Some(node_id)),
958                        tab_index: tab_index.into(),
959                        parent_node_ids: {
960                            let mut parents = Vec::new();
961                            let mut cur_parent = node_hierarchy.as_container()[node_id].parent_id();
962                            while let Some(c) = cur_parent.clone() {
963                                parents.push(NodeHierarchyItemId::from_crate_internal(Some(c)));
964                                cur_parent = node_hierarchy.as_container()[c].parent_id();
965                            }
966                            parents.reverse(); // parents sorted in depth-increasing order
967                            parents.into()
968                        },
969                    })
970                }
971            })
972            .collect()
973    }
974
975    pub fn get_computed_css_style_string(
976        &self,
977        node_data: &NodeData,
978        node_id: &NodeId,
979        node_state: &StyledNodeState,
980    ) -> String {
981        let mut s = String::new();
982        if let Some(p) = self.get_background_content(&node_data, node_id, node_state) {
983            s.push_str(&format!("background: {};", p.get_css_value_fmt()));
984        }
985        if let Some(p) = self.get_background_position(&node_data, node_id, node_state) {
986            s.push_str(&format!("background-position: {};", p.get_css_value_fmt()));
987        }
988        if let Some(p) = self.get_background_size(&node_data, node_id, node_state) {
989            s.push_str(&format!("background-size: {};", p.get_css_value_fmt()));
990        }
991        if let Some(p) = self.get_background_repeat(&node_data, node_id, node_state) {
992            s.push_str(&format!("background-repeat: {};", p.get_css_value_fmt()));
993        }
994        if let Some(p) = self.get_font_size(&node_data, node_id, node_state) {
995            s.push_str(&format!("font-size: {};", p.get_css_value_fmt()));
996        }
997        if let Some(p) = self.get_font_family(&node_data, node_id, node_state) {
998            s.push_str(&format!("font-family: {};", p.get_css_value_fmt()));
999        }
1000        if let Some(p) = self.get_text_color(&node_data, node_id, node_state) {
1001            s.push_str(&format!("color: {};", p.get_css_value_fmt()));
1002        }
1003        if let Some(p) = self.get_text_align(&node_data, node_id, node_state) {
1004            s.push_str(&format!("text-align: {};", p.get_css_value_fmt()));
1005        }
1006        if let Some(p) = self.get_line_height(&node_data, node_id, node_state) {
1007            s.push_str(&format!("line-height: {};", p.get_css_value_fmt()));
1008        }
1009        if let Some(p) = self.get_letter_spacing(&node_data, node_id, node_state) {
1010            s.push_str(&format!("letter-spacing: {};", p.get_css_value_fmt()));
1011        }
1012        if let Some(p) = self.get_word_spacing(&node_data, node_id, node_state) {
1013            s.push_str(&format!("word-spacing: {};", p.get_css_value_fmt()));
1014        }
1015        if let Some(p) = self.get_tab_width(&node_data, node_id, node_state) {
1016            s.push_str(&format!("tab-width: {};", p.get_css_value_fmt()));
1017        }
1018        if let Some(p) = self.get_cursor(&node_data, node_id, node_state) {
1019            s.push_str(&format!("cursor: {};", p.get_css_value_fmt()));
1020        }
1021        if let Some(p) = self.get_box_shadow_left(&node_data, node_id, node_state) {
1022            s.push_str(&format!(
1023                "-azul-box-shadow-left: {};",
1024                p.get_css_value_fmt()
1025            ));
1026        }
1027        if let Some(p) = self.get_box_shadow_right(&node_data, node_id, node_state) {
1028            s.push_str(&format!(
1029                "-azul-box-shadow-right: {};",
1030                p.get_css_value_fmt()
1031            ));
1032        }
1033        if let Some(p) = self.get_box_shadow_top(&node_data, node_id, node_state) {
1034            s.push_str(&format!("-azul-box-shadow-top: {};", p.get_css_value_fmt()));
1035        }
1036        if let Some(p) = self.get_box_shadow_bottom(&node_data, node_id, node_state) {
1037            s.push_str(&format!(
1038                "-azul-box-shadow-bottom: {};",
1039                p.get_css_value_fmt()
1040            ));
1041        }
1042        if let Some(p) = self.get_border_top_color(&node_data, node_id, node_state) {
1043            s.push_str(&format!("border-top-color: {};", p.get_css_value_fmt()));
1044        }
1045        if let Some(p) = self.get_border_left_color(&node_data, node_id, node_state) {
1046            s.push_str(&format!("border-left-color: {};", p.get_css_value_fmt()));
1047        }
1048        if let Some(p) = self.get_border_right_color(&node_data, node_id, node_state) {
1049            s.push_str(&format!("border-right-color: {};", p.get_css_value_fmt()));
1050        }
1051        if let Some(p) = self.get_border_bottom_color(&node_data, node_id, node_state) {
1052            s.push_str(&format!("border-bottom-color: {};", p.get_css_value_fmt()));
1053        }
1054        if let Some(p) = self.get_border_top_style(&node_data, node_id, node_state) {
1055            s.push_str(&format!("border-top-style: {};", p.get_css_value_fmt()));
1056        }
1057        if let Some(p) = self.get_border_left_style(&node_data, node_id, node_state) {
1058            s.push_str(&format!("border-left-style: {};", p.get_css_value_fmt()));
1059        }
1060        if let Some(p) = self.get_border_right_style(&node_data, node_id, node_state) {
1061            s.push_str(&format!("border-right-style: {};", p.get_css_value_fmt()));
1062        }
1063        if let Some(p) = self.get_border_bottom_style(&node_data, node_id, node_state) {
1064            s.push_str(&format!("border-bottom-style: {};", p.get_css_value_fmt()));
1065        }
1066        if let Some(p) = self.get_border_top_left_radius(&node_data, node_id, node_state) {
1067            s.push_str(&format!(
1068                "border-top-left-radius: {};",
1069                p.get_css_value_fmt()
1070            ));
1071        }
1072        if let Some(p) = self.get_border_top_right_radius(&node_data, node_id, node_state) {
1073            s.push_str(&format!(
1074                "border-top-right-radius: {};",
1075                p.get_css_value_fmt()
1076            ));
1077        }
1078        if let Some(p) = self.get_border_bottom_left_radius(&node_data, node_id, node_state) {
1079            s.push_str(&format!(
1080                "border-bottom-left-radius: {};",
1081                p.get_css_value_fmt()
1082            ));
1083        }
1084        if let Some(p) = self.get_border_bottom_right_radius(&node_data, node_id, node_state) {
1085            s.push_str(&format!(
1086                "border-bottom-right-radius: {};",
1087                p.get_css_value_fmt()
1088            ));
1089        }
1090        if let Some(p) = self.get_opacity(&node_data, node_id, node_state) {
1091            s.push_str(&format!("opacity: {};", p.get_css_value_fmt()));
1092        }
1093        if let Some(p) = self.get_transform(&node_data, node_id, node_state) {
1094            s.push_str(&format!("transform: {};", p.get_css_value_fmt()));
1095        }
1096        if let Some(p) = self.get_transform_origin(&node_data, node_id, node_state) {
1097            s.push_str(&format!("transform-origin: {};", p.get_css_value_fmt()));
1098        }
1099        if let Some(p) = self.get_perspective_origin(&node_data, node_id, node_state) {
1100            s.push_str(&format!("perspective-origin: {};", p.get_css_value_fmt()));
1101        }
1102        if let Some(p) = self.get_backface_visibility(&node_data, node_id, node_state) {
1103            s.push_str(&format!("backface-visibility: {};", p.get_css_value_fmt()));
1104        }
1105        if let Some(p) = self.get_hyphens(&node_data, node_id, node_state) {
1106            s.push_str(&format!("hyphens: {};", p.get_css_value_fmt()));
1107        }
1108        if let Some(p) = self.get_direction(&node_data, node_id, node_state) {
1109            s.push_str(&format!("direction: {};", p.get_css_value_fmt()));
1110        }
1111        if let Some(p) = self.get_white_space(&node_data, node_id, node_state) {
1112            s.push_str(&format!("white-space: {};", p.get_css_value_fmt()));
1113        }
1114        if let Some(p) = self.get_display(&node_data, node_id, node_state) {
1115            s.push_str(&format!("display: {};", p.get_css_value_fmt()));
1116        }
1117        if let Some(p) = self.get_float(&node_data, node_id, node_state) {
1118            s.push_str(&format!("float: {};", p.get_css_value_fmt()));
1119        }
1120        if let Some(p) = self.get_box_sizing(&node_data, node_id, node_state) {
1121            s.push_str(&format!("box-sizing: {};", p.get_css_value_fmt()));
1122        }
1123        if let Some(p) = self.get_width(&node_data, node_id, node_state) {
1124            s.push_str(&format!("width: {};", p.get_css_value_fmt()));
1125        }
1126        if let Some(p) = self.get_height(&node_data, node_id, node_state) {
1127            s.push_str(&format!("height: {};", p.get_css_value_fmt()));
1128        }
1129        if let Some(p) = self.get_min_width(&node_data, node_id, node_state) {
1130            s.push_str(&format!("min-width: {};", p.get_css_value_fmt()));
1131        }
1132        if let Some(p) = self.get_min_height(&node_data, node_id, node_state) {
1133            s.push_str(&format!("min-height: {};", p.get_css_value_fmt()));
1134        }
1135        if let Some(p) = self.get_max_width(&node_data, node_id, node_state) {
1136            s.push_str(&format!("max-width: {};", p.get_css_value_fmt()));
1137        }
1138        if let Some(p) = self.get_max_height(&node_data, node_id, node_state) {
1139            s.push_str(&format!("max-height: {};", p.get_css_value_fmt()));
1140        }
1141        if let Some(p) = self.get_position(&node_data, node_id, node_state) {
1142            s.push_str(&format!("position: {};", p.get_css_value_fmt()));
1143        }
1144        if let Some(p) = self.get_top(&node_data, node_id, node_state) {
1145            s.push_str(&format!("top: {};", p.get_css_value_fmt()));
1146        }
1147        if let Some(p) = self.get_bottom(&node_data, node_id, node_state) {
1148            s.push_str(&format!("bottom: {};", p.get_css_value_fmt()));
1149        }
1150        if let Some(p) = self.get_right(&node_data, node_id, node_state) {
1151            s.push_str(&format!("right: {};", p.get_css_value_fmt()));
1152        }
1153        if let Some(p) = self.get_left(&node_data, node_id, node_state) {
1154            s.push_str(&format!("left: {};", p.get_css_value_fmt()));
1155        }
1156        if let Some(p) = self.get_padding_top(&node_data, node_id, node_state) {
1157            s.push_str(&format!("padding-top: {};", p.get_css_value_fmt()));
1158        }
1159        if let Some(p) = self.get_padding_bottom(&node_data, node_id, node_state) {
1160            s.push_str(&format!("padding-bottom: {};", p.get_css_value_fmt()));
1161        }
1162        if let Some(p) = self.get_padding_left(&node_data, node_id, node_state) {
1163            s.push_str(&format!("padding-left: {};", p.get_css_value_fmt()));
1164        }
1165        if let Some(p) = self.get_padding_right(&node_data, node_id, node_state) {
1166            s.push_str(&format!("padding-right: {};", p.get_css_value_fmt()));
1167        }
1168        if let Some(p) = self.get_margin_top(&node_data, node_id, node_state) {
1169            s.push_str(&format!("margin-top: {};", p.get_css_value_fmt()));
1170        }
1171        if let Some(p) = self.get_margin_bottom(&node_data, node_id, node_state) {
1172            s.push_str(&format!("margin-bottom: {};", p.get_css_value_fmt()));
1173        }
1174        if let Some(p) = self.get_margin_left(&node_data, node_id, node_state) {
1175            s.push_str(&format!("margin-left: {};", p.get_css_value_fmt()));
1176        }
1177        if let Some(p) = self.get_margin_right(&node_data, node_id, node_state) {
1178            s.push_str(&format!("margin-right: {};", p.get_css_value_fmt()));
1179        }
1180        if let Some(p) = self.get_border_top_width(&node_data, node_id, node_state) {
1181            s.push_str(&format!("border-top-width: {};", p.get_css_value_fmt()));
1182        }
1183        if let Some(p) = self.get_border_left_width(&node_data, node_id, node_state) {
1184            s.push_str(&format!("border-left-width: {};", p.get_css_value_fmt()));
1185        }
1186        if let Some(p) = self.get_border_right_width(&node_data, node_id, node_state) {
1187            s.push_str(&format!("border-right-width: {};", p.get_css_value_fmt()));
1188        }
1189        if let Some(p) = self.get_border_bottom_width(&node_data, node_id, node_state) {
1190            s.push_str(&format!("border-bottom-width: {};", p.get_css_value_fmt()));
1191        }
1192        if let Some(p) = self.get_overflow_x(&node_data, node_id, node_state) {
1193            s.push_str(&format!("overflow-x: {};", p.get_css_value_fmt()));
1194        }
1195        if let Some(p) = self.get_overflow_y(&node_data, node_id, node_state) {
1196            s.push_str(&format!("overflow-y: {};", p.get_css_value_fmt()));
1197        }
1198        if let Some(p) = self.get_flex_direction(&node_data, node_id, node_state) {
1199            s.push_str(&format!("flex-direction: {};", p.get_css_value_fmt()));
1200        }
1201        if let Some(p) = self.get_flex_wrap(&node_data, node_id, node_state) {
1202            s.push_str(&format!("flex-wrap: {};", p.get_css_value_fmt()));
1203        }
1204        if let Some(p) = self.get_flex_grow(&node_data, node_id, node_state) {
1205            s.push_str(&format!("flex-grow: {};", p.get_css_value_fmt()));
1206        }
1207        if let Some(p) = self.get_flex_shrink(&node_data, node_id, node_state) {
1208            s.push_str(&format!("flex-shrink: {};", p.get_css_value_fmt()));
1209        }
1210        if let Some(p) = self.get_justify_content(&node_data, node_id, node_state) {
1211            s.push_str(&format!("justify-content: {};", p.get_css_value_fmt()));
1212        }
1213        if let Some(p) = self.get_align_items(&node_data, node_id, node_state) {
1214            s.push_str(&format!("align-items: {};", p.get_css_value_fmt()));
1215        }
1216        if let Some(p) = self.get_align_content(&node_data, node_id, node_state) {
1217            s.push_str(&format!("align-content: {};", p.get_css_value_fmt()));
1218        }
1219        s
1220    }
1221}
1222
1223#[repr(C)]
1224#[derive(Debug, PartialEq, Clone)]
1225pub struct CssPropertyCachePtr {
1226    pub ptr: Box<CssPropertyCache>,
1227    pub run_destructor: bool,
1228}
1229
1230impl CssPropertyCachePtr {
1231    pub fn new(cache: CssPropertyCache) -> Self {
1232        Self {
1233            ptr: Box::new(cache),
1234            run_destructor: true,
1235        }
1236    }
1237    pub fn downcast_mut<'a>(&'a mut self) -> &'a mut CssPropertyCache {
1238        &mut *self.ptr
1239    }
1240}
1241
1242impl Drop for CssPropertyCachePtr {
1243    fn drop(&mut self) {
1244        self.run_destructor = false;
1245    }
1246}
1247
1248impl CssPropertyCache {
1249    pub fn empty(node_count: usize) -> Self {
1250        Self {
1251            node_count,
1252            user_overridden_properties: BTreeMap::new(),
1253
1254            cascaded_normal_props: BTreeMap::new(),
1255            cascaded_hover_props: BTreeMap::new(),
1256            cascaded_active_props: BTreeMap::new(),
1257            cascaded_focus_props: BTreeMap::new(),
1258
1259            css_normal_props: BTreeMap::new(),
1260            css_hover_props: BTreeMap::new(),
1261            css_active_props: BTreeMap::new(),
1262            css_focus_props: BTreeMap::new(),
1263
1264            computed_values: BTreeMap::new(),
1265            dependency_chains: BTreeMap::new(),
1266        }
1267    }
1268
1269    pub fn append(&mut self, other: &mut Self) {
1270        macro_rules! append_css_property_vec {
1271            ($field_name:ident) => {{
1272                let mut s = BTreeMap::new();
1273                core::mem::swap(&mut s, &mut other.$field_name);
1274                for (node_id, property_map) in s.into_iter() {
1275                    self.$field_name
1276                        .insert(node_id + self.node_count, property_map);
1277                }
1278            }};
1279        }
1280
1281        append_css_property_vec!(user_overridden_properties);
1282        append_css_property_vec!(cascaded_normal_props);
1283        append_css_property_vec!(cascaded_hover_props);
1284        append_css_property_vec!(cascaded_active_props);
1285        append_css_property_vec!(cascaded_focus_props);
1286        append_css_property_vec!(css_normal_props);
1287        append_css_property_vec!(css_hover_props);
1288        append_css_property_vec!(css_active_props);
1289        append_css_property_vec!(css_focus_props);
1290        append_css_property_vec!(computed_values);
1291
1292        // Special handling for dependency_chains: need to adjust source_node IDs
1293        {
1294            let mut s = BTreeMap::new();
1295            core::mem::swap(&mut s, &mut other.dependency_chains);
1296            for (node_id, mut chains_map) in s.into_iter() {
1297                // Adjust the source_node IDs in each chain's steps
1298                for (_prop_type, chain) in chains_map.iter_mut() {
1299                    for step in chain.steps.iter_mut() {
1300                        match step {
1301                            CssDependencyChainStep::Em { source_node, .. } => {
1302                                *source_node = NodeId::new(source_node.index() + self.node_count);
1303                            }
1304                            CssDependencyChainStep::Percent { source_node, .. } => {
1305                                *source_node = NodeId::new(source_node.index() + self.node_count);
1306                            }
1307                            _ => {}
1308                        }
1309                    }
1310                }
1311                self.dependency_chains
1312                    .insert(node_id + self.node_count, chains_map);
1313            }
1314        }
1315
1316        self.node_count += other.node_count;
1317    }
1318
1319    pub fn is_horizontal_overflow_visible(
1320        &self,
1321        node_data: &NodeData,
1322        node_id: &NodeId,
1323        node_state: &StyledNodeState,
1324    ) -> bool {
1325        self.get_overflow_x(node_data, node_id, node_state)
1326            .and_then(|p| p.get_property_or_default())
1327            .unwrap_or_default()
1328            .is_overflow_visible()
1329    }
1330
1331    pub fn is_vertical_overflow_visible(
1332        &self,
1333        node_data: &NodeData,
1334        node_id: &NodeId,
1335        node_state: &StyledNodeState,
1336    ) -> bool {
1337        self.get_overflow_y(node_data, node_id, node_state)
1338            .and_then(|p| p.get_property_or_default())
1339            .unwrap_or_default()
1340            .is_overflow_visible()
1341    }
1342
1343    pub fn is_horizontal_overflow_hidden(
1344        &self,
1345        node_data: &NodeData,
1346        node_id: &NodeId,
1347        node_state: &StyledNodeState,
1348    ) -> bool {
1349        self.get_overflow_x(node_data, node_id, node_state)
1350            .and_then(|p| p.get_property_or_default())
1351            .unwrap_or_default()
1352            .is_overflow_hidden()
1353    }
1354
1355    pub fn is_vertical_overflow_hidden(
1356        &self,
1357        node_data: &NodeData,
1358        node_id: &NodeId,
1359        node_state: &StyledNodeState,
1360    ) -> bool {
1361        self.get_overflow_y(node_data, node_id, node_state)
1362            .and_then(|p| p.get_property_or_default())
1363            .unwrap_or_default()
1364            .is_overflow_hidden()
1365    }
1366
1367    pub fn get_text_color_or_default(
1368        &self,
1369        node_data: &NodeData,
1370        node_id: &NodeId,
1371        node_state: &StyledNodeState,
1372    ) -> StyleTextColor {
1373        use crate::ui_solver::DEFAULT_TEXT_COLOR;
1374        self.get_text_color(node_data, node_id, node_state)
1375            .and_then(|fs| fs.get_property().cloned())
1376            .unwrap_or(DEFAULT_TEXT_COLOR)
1377    }
1378
1379    /// Returns the font ID of the
1380    pub fn get_font_id_or_default(
1381        &self,
1382        node_data: &NodeData,
1383        node_id: &NodeId,
1384        node_state: &StyledNodeState,
1385    ) -> StyleFontFamilyVec {
1386        use crate::ui_solver::DEFAULT_FONT_ID;
1387        let default_font_id = vec![StyleFontFamily::System(AzString::from_const_str(
1388            DEFAULT_FONT_ID,
1389        ))]
1390        .into();
1391        let font_family_opt = self.get_font_family(node_data, node_id, node_state);
1392
1393        font_family_opt
1394            .as_ref()
1395            .and_then(|family| Some(family.get_property()?.clone()))
1396            .unwrap_or(default_font_id)
1397    }
1398
1399    pub fn get_font_size_or_default(
1400        &self,
1401        node_data: &NodeData,
1402        node_id: &NodeId,
1403        node_state: &StyledNodeState,
1404    ) -> StyleFontSize {
1405        use crate::ui_solver::DEFAULT_FONT_SIZE;
1406        self.get_font_size(node_data, node_id, node_state)
1407            .and_then(|fs| fs.get_property().cloned())
1408            .unwrap_or(DEFAULT_FONT_SIZE)
1409    }
1410
1411    pub fn has_border(
1412        &self,
1413        node_data: &NodeData,
1414        node_id: &NodeId,
1415        node_state: &StyledNodeState,
1416    ) -> bool {
1417        self.get_border_left_width(node_data, node_id, node_state)
1418            .is_some()
1419            || self
1420                .get_border_right_width(node_data, node_id, node_state)
1421                .is_some()
1422            || self
1423                .get_border_top_width(node_data, node_id, node_state)
1424                .is_some()
1425            || self
1426                .get_border_bottom_width(node_data, node_id, node_state)
1427                .is_some()
1428    }
1429
1430    pub fn has_box_shadow(
1431        &self,
1432        node_data: &NodeData,
1433        node_id: &NodeId,
1434        node_state: &StyledNodeState,
1435    ) -> bool {
1436        self.get_box_shadow_left(node_data, node_id, node_state)
1437            .is_some()
1438            || self
1439                .get_box_shadow_right(node_data, node_id, node_state)
1440                .is_some()
1441            || self
1442                .get_box_shadow_top(node_data, node_id, node_state)
1443                .is_some()
1444            || self
1445                .get_box_shadow_bottom(node_data, node_id, node_state)
1446                .is_some()
1447    }
1448
1449    pub fn get_property<'a>(
1450        &'a self,
1451        node_data: &'a NodeData,
1452        node_id: &NodeId,
1453        node_state: &StyledNodeState,
1454        css_property_type: &CssPropertyType,
1455    ) -> Option<&CssProperty> {
1456        // NOTE: This function is slow, but it is going to be called on every
1457        // node in parallel, so it should be rather fast in the end
1458
1459        use azul_css::dynamic_selector::{DynamicSelector, PseudoStateType};
1460
1461        // First test if there is some user-defined override for the property
1462        if let Some(p) = self
1463            .user_overridden_properties
1464            .get(node_id)
1465            .and_then(|n| n.get(css_property_type))
1466        {
1467            return Some(p);
1468        }
1469
1470        // Helper to check if property matches a specific pseudo state
1471        fn matches_pseudo_state(
1472            prop: &azul_css::dynamic_selector::CssPropertyWithConditions,
1473            state: PseudoStateType,
1474        ) -> bool {
1475            let conditions = prop.apply_if.as_slice();
1476            if conditions.is_empty() {
1477                state == PseudoStateType::Normal
1478            } else {
1479                conditions
1480                    .iter()
1481                    .all(|c| matches!(c, DynamicSelector::PseudoState(s) if *s == state))
1482            }
1483        }
1484
1485        // If that fails, see if there is an inline CSS property that matches
1486        // :focus > :active > :hover > normal (fallback)
1487        if node_state.focused {
1488            // PRIORITY 1: Inline CSS properties (highest priority per CSS spec)
1489            if let Some(p) = node_data.css_props.as_ref().iter().find_map(|css_prop| {
1490                if matches_pseudo_state(css_prop, PseudoStateType::Focus)
1491                    && css_prop.property.get_type() == *css_property_type
1492                {
1493                    Some(&css_prop.property)
1494                } else {
1495                    None
1496                }
1497            }) {
1498                return Some(p);
1499            }
1500
1501            // PRIORITY 2: CSS stylesheet properties
1502            if let Some(p) = self
1503                .css_focus_props
1504                .get(node_id)
1505                .and_then(|map| map.get(css_property_type))
1506            {
1507                return Some(p);
1508            }
1509
1510            // PRIORITY 3: Cascaded/inherited properties
1511            if let Some(p) = self
1512                .cascaded_focus_props
1513                .get(node_id)
1514                .and_then(|map| map.get(css_property_type))
1515            {
1516                return Some(p);
1517            }
1518        }
1519
1520        if node_state.active {
1521            // PRIORITY 1: Inline CSS properties (highest priority per CSS spec)
1522            if let Some(p) = node_data.css_props.as_ref().iter().find_map(|css_prop| {
1523                if matches_pseudo_state(css_prop, PseudoStateType::Active)
1524                    && css_prop.property.get_type() == *css_property_type
1525                {
1526                    Some(&css_prop.property)
1527                } else {
1528                    None
1529                }
1530            }) {
1531                return Some(p);
1532            }
1533
1534            // PRIORITY 2: CSS stylesheet properties
1535            if let Some(p) = self
1536                .css_active_props
1537                .get(node_id)
1538                .and_then(|map| map.get(css_property_type))
1539            {
1540                return Some(p);
1541            }
1542
1543            // PRIORITY 3: Cascaded/inherited properties
1544            if let Some(p) = self
1545                .cascaded_active_props
1546                .get(node_id)
1547                .and_then(|map| map.get(css_property_type))
1548            {
1549                return Some(p);
1550            }
1551        }
1552
1553        if node_state.hover {
1554            // PRIORITY 1: Inline CSS properties (highest priority per CSS spec)
1555            if let Some(p) = node_data.css_props.as_ref().iter().find_map(|css_prop| {
1556                if matches_pseudo_state(css_prop, PseudoStateType::Hover)
1557                    && css_prop.property.get_type() == *css_property_type
1558                {
1559                    Some(&css_prop.property)
1560                } else {
1561                    None
1562                }
1563            }) {
1564                return Some(p);
1565            }
1566
1567            // PRIORITY 2: CSS stylesheet properties
1568            if let Some(p) = self
1569                .css_hover_props
1570                .get(node_id)
1571                .and_then(|map| map.get(css_property_type))
1572            {
1573                return Some(p);
1574            }
1575
1576            // PRIORITY 3: Cascaded/inherited properties
1577            if let Some(p) = self
1578                .cascaded_hover_props
1579                .get(node_id)
1580                .and_then(|map| map.get(css_property_type))
1581            {
1582                return Some(p);
1583            }
1584        }
1585
1586        // Normal/fallback properties - always apply as base layer
1587        // PRIORITY 1: Inline CSS properties (highest priority per CSS spec)
1588        if let Some(p) = node_data.css_props.as_ref().iter().find_map(|css_prop| {
1589            if matches_pseudo_state(css_prop, PseudoStateType::Normal)
1590                && css_prop.property.get_type() == *css_property_type
1591            {
1592                Some(&css_prop.property)
1593            } else {
1594                None
1595            }
1596        }) {
1597            return Some(p);
1598        }
1599
1600        // PRIORITY 2: CSS stylesheet properties
1601        if let Some(p) = self
1602            .css_normal_props
1603            .get(node_id)
1604            .and_then(|map| map.get(css_property_type))
1605        {
1606            return Some(p);
1607        }
1608
1609        // PRIORITY 3: Cascaded/inherited properties
1610        if let Some(p) = self
1611            .cascaded_normal_props
1612            .get(node_id)
1613            .and_then(|map| map.get(css_property_type))
1614        {
1615            return Some(p);
1616        }
1617
1618        // NEW: Check computed values cache for inherited properties
1619        // This provides efficient access to pre-resolved inherited values
1620        // without needing to walk up the tree
1621        if css_property_type.is_inheritable() {
1622            if let Some(prop_with_origin) = self
1623                .computed_values
1624                .get(node_id)
1625                .and_then(|map| map.get(css_property_type))
1626            {
1627                return Some(&prop_with_origin.property);
1628            }
1629        }
1630
1631        // User-agent stylesheet fallback (lowest precedence)
1632        // Check if the node type has a default value for this property
1633        crate::ua_css::get_ua_property(&node_data.node_type, *css_property_type)
1634    }
1635
1636    /// Get a CSS property using DynamicSelectorContext for evaluation.
1637    ///
1638    /// This is the new API that supports @media queries, @container queries,
1639    /// OS-specific styles, and all pseudo-states via `CssPropertyWithConditions`.
1640    ///
1641    /// The evaluation follows "last wins" semantics - properties are evaluated
1642    /// in reverse order and the first matching property wins.
1643    pub fn get_property_with_context<'a>(
1644        &'a self,
1645        node_data: &'a NodeData,
1646        node_id: &NodeId,
1647        context: &DynamicSelectorContext,
1648        css_property_type: &CssPropertyType,
1649    ) -> Option<&CssProperty> {
1650        // First test if there is some user-defined override for the property
1651        if let Some(p) = self
1652            .user_overridden_properties
1653            .get(node_id)
1654            .and_then(|n| n.get(css_property_type))
1655        {
1656            return Some(p);
1657        }
1658
1659        // Check inline CSS properties with DynamicSelectorContext evaluation.
1660        // Iterate in REVERSE order - "last found wins" semantics.
1661        // This replaces the old Focus > Active > Hover > Normal priority chain.
1662        if let Some(prop_with_conditions) =
1663            node_data.css_props.as_ref().iter().rev().find(|prop| {
1664                prop.property.get_type() == *css_property_type && prop.matches(context)
1665            })
1666        {
1667            return Some(&prop_with_conditions.property);
1668        }
1669
1670        // Fall back to CSS file and cascaded properties
1671        let legacy_state = StyledNodeState::from_pseudo_state_flags(&context.pseudo_state);
1672        if let Some(p) = self.get_property(node_data, node_id, &legacy_state, css_property_type) {
1673            return Some(p);
1674        }
1675
1676        None
1677    }
1678
1679    /// Check if any properties with conditions would change between two contexts.
1680    /// This is used for re-layout detection on viewport/container resize.
1681    pub fn check_properties_changed(
1682        node_data: &NodeData,
1683        old_context: &DynamicSelectorContext,
1684        new_context: &DynamicSelectorContext,
1685    ) -> bool {
1686        for prop in node_data.css_props.as_ref().iter() {
1687            let was_active = prop.matches(old_context);
1688            let is_active = prop.matches(new_context);
1689            if was_active != is_active {
1690                return true;
1691            }
1692        }
1693        false
1694    }
1695
1696    /// Check if any layout-affecting properties would change between two contexts.
1697    /// This is a more targeted check for re-layout detection.
1698    pub fn check_layout_properties_changed(
1699        node_data: &NodeData,
1700        old_context: &DynamicSelectorContext,
1701        new_context: &DynamicSelectorContext,
1702    ) -> bool {
1703        for prop in node_data.css_props.as_ref().iter() {
1704            // Skip non-layout-affecting properties
1705            if !prop.is_layout_affecting() {
1706                continue;
1707            }
1708
1709            let was_active = prop.matches(old_context);
1710            let is_active = prop.matches(new_context);
1711            if was_active != is_active {
1712                return true;
1713            }
1714        }
1715        false
1716    }
1717
1718    pub fn get_background_content<'a>(
1719        &'a self,
1720        node_data: &'a NodeData,
1721        node_id: &NodeId,
1722        node_state: &StyledNodeState,
1723    ) -> Option<&'a StyleBackgroundContentVecValue> {
1724        self.get_property(
1725            node_data,
1726            node_id,
1727            node_state,
1728            &CssPropertyType::BackgroundContent,
1729        )
1730        .and_then(|p| p.as_background_content())
1731    }
1732
1733    // Method for getting hyphens property
1734    pub fn get_hyphens<'a>(
1735        &'a self,
1736        node_data: &'a NodeData,
1737        node_id: &NodeId,
1738        node_state: &StyledNodeState,
1739    ) -> Option<&'a StyleHyphensValue> {
1740        self.get_property(node_data, node_id, node_state, &CssPropertyType::Hyphens)
1741            .and_then(|p| p.as_hyphens())
1742    }
1743
1744    // Method for getting direction property
1745    pub fn get_direction<'a>(
1746        &'a self,
1747        node_data: &'a NodeData,
1748        node_id: &NodeId,
1749        node_state: &StyledNodeState,
1750    ) -> Option<&'a StyleDirectionValue> {
1751        self.get_property(node_data, node_id, node_state, &CssPropertyType::Direction)
1752            .and_then(|p| p.as_direction())
1753    }
1754
1755    // Method for getting white-space property
1756    pub fn get_white_space<'a>(
1757        &'a self,
1758        node_data: &'a NodeData,
1759        node_id: &NodeId,
1760        node_state: &StyledNodeState,
1761    ) -> Option<&'a StyleWhiteSpaceValue> {
1762        self.get_property(node_data, node_id, node_state, &CssPropertyType::WhiteSpace)
1763            .and_then(|p| p.as_white_space())
1764    }
1765    pub fn get_background_position<'a>(
1766        &'a self,
1767        node_data: &'a NodeData,
1768        node_id: &NodeId,
1769        node_state: &StyledNodeState,
1770    ) -> Option<&'a StyleBackgroundPositionVecValue> {
1771        self.get_property(
1772            node_data,
1773            node_id,
1774            node_state,
1775            &CssPropertyType::BackgroundPosition,
1776        )
1777        .and_then(|p| p.as_background_position())
1778    }
1779    pub fn get_background_size<'a>(
1780        &'a self,
1781        node_data: &'a NodeData,
1782        node_id: &NodeId,
1783        node_state: &StyledNodeState,
1784    ) -> Option<&'a StyleBackgroundSizeVecValue> {
1785        self.get_property(
1786            node_data,
1787            node_id,
1788            node_state,
1789            &CssPropertyType::BackgroundSize,
1790        )
1791        .and_then(|p| p.as_background_size())
1792    }
1793    pub fn get_background_repeat<'a>(
1794        &'a self,
1795        node_data: &'a NodeData,
1796        node_id: &NodeId,
1797        node_state: &StyledNodeState,
1798    ) -> Option<&'a StyleBackgroundRepeatVecValue> {
1799        self.get_property(
1800            node_data,
1801            node_id,
1802            node_state,
1803            &CssPropertyType::BackgroundRepeat,
1804        )
1805        .and_then(|p| p.as_background_repeat())
1806    }
1807    pub fn get_font_size<'a>(
1808        &'a self,
1809        node_data: &'a NodeData,
1810        node_id: &NodeId,
1811        node_state: &StyledNodeState,
1812    ) -> Option<&'a StyleFontSizeValue> {
1813        self.get_property(node_data, node_id, node_state, &CssPropertyType::FontSize)
1814            .and_then(|p| p.as_font_size())
1815    }
1816    pub fn get_font_family<'a>(
1817        &'a self,
1818        node_data: &'a NodeData,
1819        node_id: &NodeId,
1820        node_state: &StyledNodeState,
1821    ) -> Option<&'a StyleFontFamilyVecValue> {
1822        self.get_property(node_data, node_id, node_state, &CssPropertyType::FontFamily)
1823            .and_then(|p| p.as_font_family())
1824    }
1825    pub fn get_font_weight<'a>(
1826        &'a self,
1827        node_data: &'a NodeData,
1828        node_id: &NodeId,
1829        node_state: &StyledNodeState,
1830    ) -> Option<&'a StyleFontWeightValue> {
1831        self.get_property(node_data, node_id, node_state, &CssPropertyType::FontWeight)
1832            .and_then(|p| p.as_font_weight())
1833    }
1834    pub fn get_font_style<'a>(
1835        &'a self,
1836        node_data: &'a NodeData,
1837        node_id: &NodeId,
1838        node_state: &StyledNodeState,
1839    ) -> Option<&'a StyleFontStyleValue> {
1840        self.get_property(node_data, node_id, node_state, &CssPropertyType::FontStyle)
1841            .and_then(|p| p.as_font_style())
1842    }
1843    pub fn get_text_color<'a>(
1844        &'a self,
1845        node_data: &'a NodeData,
1846        node_id: &NodeId,
1847        node_state: &StyledNodeState,
1848    ) -> Option<&'a StyleTextColorValue> {
1849        self.get_property(node_data, node_id, node_state, &CssPropertyType::TextColor)
1850            .and_then(|p| p.as_text_color())
1851    }
1852    // Method for getting text-indent property
1853    pub fn get_text_indent<'a>(
1854        &'a self,
1855        node_data: &'a NodeData,
1856        node_id: &NodeId,
1857        node_state: &StyledNodeState,
1858    ) -> Option<&'a StyleTextIndentValue> {
1859        self.get_property(node_data, node_id, node_state, &CssPropertyType::TextIndent)
1860            .and_then(|p| p.as_text_indent())
1861    }
1862    // Method for getting initial-letter property
1863    pub fn get_initial_letter<'a>(
1864        &'a self,
1865        node_data: &'a NodeData,
1866        node_id: &NodeId,
1867        node_state: &StyledNodeState,
1868    ) -> Option<&'a StyleInitialLetterValue> {
1869        self.get_property(
1870            node_data,
1871            node_id,
1872            node_state,
1873            &CssPropertyType::InitialLetter,
1874        )
1875        .and_then(|p| p.as_initial_letter())
1876    }
1877    // Method for getting line-clamp property
1878    pub fn get_line_clamp<'a>(
1879        &'a self,
1880        node_data: &'a NodeData,
1881        node_id: &NodeId,
1882        node_state: &StyledNodeState,
1883    ) -> Option<&'a StyleLineClampValue> {
1884        self.get_property(node_data, node_id, node_state, &CssPropertyType::LineClamp)
1885            .and_then(|p| p.as_line_clamp())
1886    }
1887    // Method for getting hanging-punctuation property
1888    pub fn get_hanging_punctuation<'a>(
1889        &'a self,
1890        node_data: &'a NodeData,
1891        node_id: &NodeId,
1892        node_state: &StyledNodeState,
1893    ) -> Option<&'a StyleHangingPunctuationValue> {
1894        self.get_property(
1895            node_data,
1896            node_id,
1897            node_state,
1898            &CssPropertyType::HangingPunctuation,
1899        )
1900        .and_then(|p| p.as_hanging_punctuation())
1901    }
1902    // Method for getting text-combine-upright property
1903    pub fn get_text_combine_upright<'a>(
1904        &'a self,
1905        node_data: &'a NodeData,
1906        node_id: &NodeId,
1907        node_state: &StyledNodeState,
1908    ) -> Option<&'a StyleTextCombineUprightValue> {
1909        self.get_property(
1910            node_data,
1911            node_id,
1912            node_state,
1913            &CssPropertyType::TextCombineUpright,
1914        )
1915        .and_then(|p| p.as_text_combine_upright())
1916    }
1917    // Method for getting -azul-exclusion-margin property
1918    pub fn get_exclusion_margin<'a>(
1919        &'a self,
1920        node_data: &'a NodeData,
1921        node_id: &NodeId,
1922        node_state: &StyledNodeState,
1923    ) -> Option<&'a StyleExclusionMarginValue> {
1924        self.get_property(
1925            node_data,
1926            node_id,
1927            node_state,
1928            &CssPropertyType::ExclusionMargin,
1929        )
1930        .and_then(|p| p.as_exclusion_margin())
1931    }
1932    // Method for getting -azul-hyphenation-language property
1933    pub fn get_hyphenation_language<'a>(
1934        &'a self,
1935        node_data: &'a NodeData,
1936        node_id: &NodeId,
1937        node_state: &StyledNodeState,
1938    ) -> Option<&'a StyleHyphenationLanguageValue> {
1939        self.get_property(
1940            node_data,
1941            node_id,
1942            node_state,
1943            &CssPropertyType::HyphenationLanguage,
1944        )
1945        .and_then(|p| p.as_hyphenation_language())
1946    }
1947    // Method for getting caret-color property
1948    pub fn get_caret_color<'a>(
1949        &'a self,
1950        node_data: &'a NodeData,
1951        node_id: &NodeId,
1952        node_state: &StyledNodeState,
1953    ) -> Option<&'a CaretColorValue> {
1954        self.get_property(node_data, node_id, node_state, &CssPropertyType::CaretColor)
1955            .and_then(|p| p.as_caret_color())
1956    }
1957
1958    // Method for getting -azul-caret-width property
1959    pub fn get_caret_width<'a>(
1960        &'a self,
1961        node_data: &'a NodeData,
1962        node_id: &NodeId,
1963        node_state: &StyledNodeState,
1964    ) -> Option<&'a CaretWidthValue> {
1965        self.get_property(node_data, node_id, node_state, &CssPropertyType::CaretWidth)
1966            .and_then(|p| p.as_caret_width())
1967    }
1968
1969    // Method for getting caret-animation-duration property
1970    pub fn get_caret_animation_duration<'a>(
1971        &'a self,
1972        node_data: &'a NodeData,
1973        node_id: &NodeId,
1974        node_state: &StyledNodeState,
1975    ) -> Option<&'a CaretAnimationDurationValue> {
1976        self.get_property(
1977            node_data,
1978            node_id,
1979            node_state,
1980            &CssPropertyType::CaretAnimationDuration,
1981        )
1982        .and_then(|p| p.as_caret_animation_duration())
1983    }
1984
1985    // Method for getting selection-background-color property
1986    pub fn get_selection_background_color<'a>(
1987        &'a self,
1988        node_data: &'a NodeData,
1989        node_id: &NodeId,
1990        node_state: &StyledNodeState,
1991    ) -> Option<&'a SelectionBackgroundColorValue> {
1992        self.get_property(
1993            node_data,
1994            node_id,
1995            node_state,
1996            &CssPropertyType::SelectionBackgroundColor,
1997        )
1998        .and_then(|p| p.as_selection_background_color())
1999    }
2000
2001    // Method for getting selection-color property
2002    pub fn get_selection_color<'a>(
2003        &'a self,
2004        node_data: &'a NodeData,
2005        node_id: &NodeId,
2006        node_state: &StyledNodeState,
2007    ) -> Option<&'a SelectionColorValue> {
2008        self.get_property(
2009            node_data,
2010            node_id,
2011            node_state,
2012            &CssPropertyType::SelectionColor,
2013        )
2014        .and_then(|p| p.as_selection_color())
2015    }
2016
2017    // Method for getting -azul-selection-radius property
2018    pub fn get_selection_radius<'a>(
2019        &'a self,
2020        node_data: &'a NodeData,
2021        node_id: &NodeId,
2022        node_state: &StyledNodeState,
2023    ) -> Option<&'a SelectionRadiusValue> {
2024        self.get_property(
2025            node_data,
2026            node_id,
2027            node_state,
2028            &CssPropertyType::SelectionRadius,
2029        )
2030        .and_then(|p| p.as_selection_radius())
2031    }
2032
2033    // Method for getting text-justify property
2034    pub fn get_text_justify<'a>(
2035        &'a self,
2036        node_data: &'a NodeData,
2037        node_id: &NodeId,
2038        node_state: &StyledNodeState,
2039    ) -> Option<&'a LayoutTextJustifyValue> {
2040        self.get_property(
2041            node_data,
2042            node_id,
2043            node_state,
2044            &CssPropertyType::TextJustify,
2045        )
2046        .and_then(|p| p.as_text_justify())
2047    }
2048
2049    // Method for getting z-index property
2050    pub fn get_z_index<'a>(
2051        &'a self,
2052        node_data: &'a NodeData,
2053        node_id: &NodeId,
2054        node_state: &StyledNodeState,
2055    ) -> Option<&'a LayoutZIndexValue> {
2056        self.get_property(node_data, node_id, node_state, &CssPropertyType::ZIndex)
2057            .and_then(|p| p.as_z_index())
2058    }
2059
2060    // Method for getting flex-basis property
2061    pub fn get_flex_basis<'a>(
2062        &'a self,
2063        node_data: &'a NodeData,
2064        node_id: &NodeId,
2065        node_state: &StyledNodeState,
2066    ) -> Option<&'a LayoutFlexBasisValue> {
2067        self.get_property(node_data, node_id, node_state, &CssPropertyType::FlexBasis)
2068            .and_then(|p| p.as_flex_basis())
2069    }
2070
2071    // Method for getting column-gap property
2072    pub fn get_column_gap<'a>(
2073        &'a self,
2074        node_data: &'a NodeData,
2075        node_id: &NodeId,
2076        node_state: &StyledNodeState,
2077    ) -> Option<&'a LayoutColumnGapValue> {
2078        self.get_property(node_data, node_id, node_state, &CssPropertyType::ColumnGap)
2079            .and_then(|p| p.as_column_gap())
2080    }
2081
2082    // Method for getting row-gap property
2083    pub fn get_row_gap<'a>(
2084        &'a self,
2085        node_data: &'a NodeData,
2086        node_id: &NodeId,
2087        node_state: &StyledNodeState,
2088    ) -> Option<&'a LayoutRowGapValue> {
2089        self.get_property(node_data, node_id, node_state, &CssPropertyType::RowGap)
2090            .and_then(|p| p.as_row_gap())
2091    }
2092
2093    // Method for getting grid-template-columns property
2094    pub fn get_grid_template_columns<'a>(
2095        &'a self,
2096        node_data: &'a NodeData,
2097        node_id: &NodeId,
2098        node_state: &StyledNodeState,
2099    ) -> Option<&'a LayoutGridTemplateColumnsValue> {
2100        self.get_property(
2101            node_data,
2102            node_id,
2103            node_state,
2104            &CssPropertyType::GridTemplateColumns,
2105        )
2106        .and_then(|p| p.as_grid_template_columns())
2107    }
2108
2109    // Method for getting grid-template-rows property
2110    pub fn get_grid_template_rows<'a>(
2111        &'a self,
2112        node_data: &'a NodeData,
2113        node_id: &NodeId,
2114        node_state: &StyledNodeState,
2115    ) -> Option<&'a LayoutGridTemplateRowsValue> {
2116        self.get_property(
2117            node_data,
2118            node_id,
2119            node_state,
2120            &CssPropertyType::GridTemplateRows,
2121        )
2122        .and_then(|p| p.as_grid_template_rows())
2123    }
2124
2125    // Method for getting grid-auto-columns property
2126    pub fn get_grid_auto_columns<'a>(
2127        &'a self,
2128        node_data: &'a NodeData,
2129        node_id: &NodeId,
2130        node_state: &StyledNodeState,
2131    ) -> Option<&'a LayoutGridAutoColumnsValue> {
2132        self.get_property(
2133            node_data,
2134            node_id,
2135            node_state,
2136            &CssPropertyType::GridAutoColumns,
2137        )
2138        .and_then(|p| p.as_grid_auto_columns())
2139    }
2140
2141    // Method for getting grid-auto-rows property
2142    pub fn get_grid_auto_rows<'a>(
2143        &'a self,
2144        node_data: &'a NodeData,
2145        node_id: &NodeId,
2146        node_state: &StyledNodeState,
2147    ) -> Option<&'a LayoutGridAutoRowsValue> {
2148        self.get_property(
2149            node_data,
2150            node_id,
2151            node_state,
2152            &CssPropertyType::GridAutoRows,
2153        )
2154        .and_then(|p| p.as_grid_auto_rows())
2155    }
2156
2157    // Method for getting grid-column property
2158    pub fn get_grid_column<'a>(
2159        &'a self,
2160        node_data: &'a NodeData,
2161        node_id: &NodeId,
2162        node_state: &StyledNodeState,
2163    ) -> Option<&'a LayoutGridColumnValue> {
2164        self.get_property(node_data, node_id, node_state, &CssPropertyType::GridColumn)
2165            .and_then(|p| p.as_grid_column())
2166    }
2167
2168    // Method for getting grid-row property
2169    pub fn get_grid_row<'a>(
2170        &'a self,
2171        node_data: &'a NodeData,
2172        node_id: &NodeId,
2173        node_state: &StyledNodeState,
2174    ) -> Option<&'a LayoutGridRowValue> {
2175        self.get_property(node_data, node_id, node_state, &CssPropertyType::GridRow)
2176            .and_then(|p| p.as_grid_row())
2177    }
2178
2179    // Method for getting grid-auto-flow property
2180    pub fn get_grid_auto_flow<'a>(
2181        &'a self,
2182        node_data: &'a NodeData,
2183        node_id: &NodeId,
2184        node_state: &StyledNodeState,
2185    ) -> Option<&'a LayoutGridAutoFlowValue> {
2186        self.get_property(
2187            node_data,
2188            node_id,
2189            node_state,
2190            &CssPropertyType::GridAutoFlow,
2191        )
2192        .and_then(|p| p.as_grid_auto_flow())
2193    }
2194
2195    // Method for getting justify-self property
2196    pub fn get_justify_self<'a>(
2197        &'a self,
2198        node_data: &'a NodeData,
2199        node_id: &NodeId,
2200        node_state: &StyledNodeState,
2201    ) -> Option<&'a LayoutJustifySelfValue> {
2202        self.get_property(
2203            node_data,
2204            node_id,
2205            node_state,
2206            &CssPropertyType::JustifySelf,
2207        )
2208        .and_then(|p| p.as_justify_self())
2209    }
2210
2211    // Method for getting justify-items property
2212    pub fn get_justify_items<'a>(
2213        &'a self,
2214        node_data: &'a NodeData,
2215        node_id: &NodeId,
2216        node_state: &StyledNodeState,
2217    ) -> Option<&'a LayoutJustifyItemsValue> {
2218        self.get_property(
2219            node_data,
2220            node_id,
2221            node_state,
2222            &CssPropertyType::JustifyItems,
2223        )
2224        .and_then(|p| p.as_justify_items())
2225    }
2226
2227    // Method for getting gap property
2228    pub fn get_gap<'a>(
2229        &'a self,
2230        node_data: &'a NodeData,
2231        node_id: &NodeId,
2232        node_state: &StyledNodeState,
2233    ) -> Option<&'a LayoutGapValue> {
2234        self.get_property(node_data, node_id, node_state, &CssPropertyType::Gap)
2235            .and_then(|p| p.as_gap())
2236    }
2237
2238    // Method for getting grid-gap property
2239    pub fn get_grid_gap<'a>(
2240        &'a self,
2241        node_data: &'a NodeData,
2242        node_id: &NodeId,
2243        node_state: &StyledNodeState,
2244    ) -> Option<&'a LayoutGapValue> {
2245        self.get_property(node_data, node_id, node_state, &CssPropertyType::GridGap)
2246            .and_then(|p| p.as_grid_gap())
2247    }
2248
2249    // Method for getting align-self property
2250    pub fn get_align_self<'a>(
2251        &'a self,
2252        node_data: &'a NodeData,
2253        node_id: &NodeId,
2254        node_state: &StyledNodeState,
2255    ) -> Option<&'a LayoutAlignSelfValue> {
2256        self.get_property(node_data, node_id, node_state, &CssPropertyType::AlignSelf)
2257            .and_then(|p| p.as_align_self())
2258    }
2259
2260    // Method for getting font property
2261    pub fn get_font<'a>(
2262        &'a self,
2263        node_data: &'a NodeData,
2264        node_id: &NodeId,
2265        node_state: &StyledNodeState,
2266    ) -> Option<&'a StyleFontValue> {
2267        self.get_property(node_data, node_id, node_state, &CssPropertyType::Font)
2268            .and_then(|p| p.as_font())
2269    }
2270
2271    // Method for getting writing-mode property
2272    pub fn get_writing_mode<'a>(
2273        &'a self,
2274        node_data: &'a NodeData,
2275        node_id: &NodeId,
2276        node_state: &StyledNodeState,
2277    ) -> Option<&'a LayoutWritingModeValue> {
2278        self.get_property(
2279            node_data,
2280            node_id,
2281            node_state,
2282            &CssPropertyType::WritingMode,
2283        )
2284        .and_then(|p| p.as_writing_mode())
2285    }
2286
2287    // Method for getting clear property
2288    pub fn get_clear<'a>(
2289        &'a self,
2290        node_data: &'a NodeData,
2291        node_id: &NodeId,
2292        node_state: &StyledNodeState,
2293    ) -> Option<&'a LayoutClearValue> {
2294        self.get_property(node_data, node_id, node_state, &CssPropertyType::Clear)
2295            .and_then(|p| p.as_clear())
2296    }
2297
2298    // Method for getting shape-outside property
2299    pub fn get_shape_outside<'a>(
2300        &'a self,
2301        node_data: &'a NodeData,
2302        node_id: &NodeId,
2303        node_state: &StyledNodeState,
2304    ) -> Option<&'a ShapeOutsideValue> {
2305        self.get_property(
2306            node_data,
2307            node_id,
2308            node_state,
2309            &CssPropertyType::ShapeOutside,
2310        )
2311        .and_then(|p| p.as_shape_outside())
2312    }
2313
2314    // Method for getting shape-inside property
2315    pub fn get_shape_inside<'a>(
2316        &'a self,
2317        node_data: &'a NodeData,
2318        node_id: &NodeId,
2319        node_state: &StyledNodeState,
2320    ) -> Option<&'a ShapeInsideValue> {
2321        self.get_property(
2322            node_data,
2323            node_id,
2324            node_state,
2325            &CssPropertyType::ShapeInside,
2326        )
2327        .and_then(|p| p.as_shape_inside())
2328    }
2329
2330    // Method for getting clip-path property
2331    pub fn get_clip_path<'a>(
2332        &'a self,
2333        node_data: &'a NodeData,
2334        node_id: &NodeId,
2335        node_state: &StyledNodeState,
2336    ) -> Option<&'a ClipPathValue> {
2337        self.get_property(node_data, node_id, node_state, &CssPropertyType::ClipPath)
2338            .and_then(|p| p.as_clip_path())
2339    }
2340
2341    // Method for getting scrollbar-style property
2342    pub fn get_scrollbar_style<'a>(
2343        &'a self,
2344        node_data: &'a NodeData,
2345        node_id: &NodeId,
2346        node_state: &StyledNodeState,
2347    ) -> Option<&'a ScrollbarStyleValue> {
2348        self.get_property(node_data, node_id, node_state, &CssPropertyType::Scrollbar)
2349            .and_then(|p| p.as_scrollbar())
2350    }
2351
2352    // Method for getting scrollbar-width property
2353    pub fn get_scrollbar_width<'a>(
2354        &'a self,
2355        node_data: &'a NodeData,
2356        node_id: &NodeId,
2357        node_state: &StyledNodeState,
2358    ) -> Option<&'a LayoutScrollbarWidthValue> {
2359        self.get_property(
2360            node_data,
2361            node_id,
2362            node_state,
2363            &CssPropertyType::ScrollbarWidth,
2364        )
2365        .and_then(|p| p.as_scrollbar_width())
2366    }
2367
2368    // Method for getting scrollbar-color property
2369    pub fn get_scrollbar_color<'a>(
2370        &'a self,
2371        node_data: &'a NodeData,
2372        node_id: &NodeId,
2373        node_state: &StyledNodeState,
2374    ) -> Option<&'a StyleScrollbarColorValue> {
2375        self.get_property(
2376            node_data,
2377            node_id,
2378            node_state,
2379            &CssPropertyType::ScrollbarColor,
2380        )
2381        .and_then(|p| p.as_scrollbar_color())
2382    }
2383
2384    // Method for getting visibility property
2385    pub fn get_visibility<'a>(
2386        &'a self,
2387        node_data: &'a NodeData,
2388        node_id: &NodeId,
2389        node_state: &StyledNodeState,
2390    ) -> Option<&'a StyleVisibilityValue> {
2391        self.get_property(node_data, node_id, node_state, &CssPropertyType::Visibility)
2392            .and_then(|p| p.as_visibility())
2393    }
2394
2395    // Method for getting break-before property
2396    pub fn get_break_before<'a>(
2397        &'a self,
2398        node_data: &'a NodeData,
2399        node_id: &NodeId,
2400        node_state: &StyledNodeState,
2401    ) -> Option<&'a PageBreakValue> {
2402        self.get_property(
2403            node_data,
2404            node_id,
2405            node_state,
2406            &CssPropertyType::BreakBefore,
2407        )
2408        .and_then(|p| p.as_break_before())
2409    }
2410
2411    // Method for getting break-after property
2412    pub fn get_break_after<'a>(
2413        &'a self,
2414        node_data: &'a NodeData,
2415        node_id: &NodeId,
2416        node_state: &StyledNodeState,
2417    ) -> Option<&'a PageBreakValue> {
2418        self.get_property(node_data, node_id, node_state, &CssPropertyType::BreakAfter)
2419            .and_then(|p| p.as_break_after())
2420    }
2421
2422    // Method for getting break-inside property
2423    pub fn get_break_inside<'a>(
2424        &'a self,
2425        node_data: &'a NodeData,
2426        node_id: &NodeId,
2427        node_state: &StyledNodeState,
2428    ) -> Option<&'a BreakInsideValue> {
2429        self.get_property(
2430            node_data,
2431            node_id,
2432            node_state,
2433            &CssPropertyType::BreakInside,
2434        )
2435        .and_then(|p| p.as_break_inside())
2436    }
2437
2438    // Method for getting orphans property
2439    pub fn get_orphans<'a>(
2440        &'a self,
2441        node_data: &'a NodeData,
2442        node_id: &NodeId,
2443        node_state: &StyledNodeState,
2444    ) -> Option<&'a OrphansValue> {
2445        self.get_property(node_data, node_id, node_state, &CssPropertyType::Orphans)
2446            .and_then(|p| p.as_orphans())
2447    }
2448
2449    // Method for getting widows property
2450    pub fn get_widows<'a>(
2451        &'a self,
2452        node_data: &'a NodeData,
2453        node_id: &NodeId,
2454        node_state: &StyledNodeState,
2455    ) -> Option<&'a WidowsValue> {
2456        self.get_property(node_data, node_id, node_state, &CssPropertyType::Widows)
2457            .and_then(|p| p.as_widows())
2458    }
2459
2460    // Method for getting box-decoration-break property
2461    pub fn get_box_decoration_break<'a>(
2462        &'a self,
2463        node_data: &'a NodeData,
2464        node_id: &NodeId,
2465        node_state: &StyledNodeState,
2466    ) -> Option<&'a BoxDecorationBreakValue> {
2467        self.get_property(
2468            node_data,
2469            node_id,
2470            node_state,
2471            &CssPropertyType::BoxDecorationBreak,
2472        )
2473        .and_then(|p| p.as_box_decoration_break())
2474    }
2475
2476    // Method for getting column-count property
2477    pub fn get_column_count<'a>(
2478        &'a self,
2479        node_data: &'a NodeData,
2480        node_id: &NodeId,
2481        node_state: &StyledNodeState,
2482    ) -> Option<&'a ColumnCountValue> {
2483        self.get_property(
2484            node_data,
2485            node_id,
2486            node_state,
2487            &CssPropertyType::ColumnCount,
2488        )
2489        .and_then(|p| p.as_column_count())
2490    }
2491
2492    // Method for getting column-width property
2493    pub fn get_column_width<'a>(
2494        &'a self,
2495        node_data: &'a NodeData,
2496        node_id: &NodeId,
2497        node_state: &StyledNodeState,
2498    ) -> Option<&'a ColumnWidthValue> {
2499        self.get_property(
2500            node_data,
2501            node_id,
2502            node_state,
2503            &CssPropertyType::ColumnWidth,
2504        )
2505        .and_then(|p| p.as_column_width())
2506    }
2507
2508    // Method for getting column-span property
2509    pub fn get_column_span<'a>(
2510        &'a self,
2511        node_data: &'a NodeData,
2512        node_id: &NodeId,
2513        node_state: &StyledNodeState,
2514    ) -> Option<&'a ColumnSpanValue> {
2515        self.get_property(node_data, node_id, node_state, &CssPropertyType::ColumnSpan)
2516            .and_then(|p| p.as_column_span())
2517    }
2518
2519    // Method for getting column-fill property
2520    pub fn get_column_fill<'a>(
2521        &'a self,
2522        node_data: &'a NodeData,
2523        node_id: &NodeId,
2524        node_state: &StyledNodeState,
2525    ) -> Option<&'a ColumnFillValue> {
2526        self.get_property(node_data, node_id, node_state, &CssPropertyType::ColumnFill)
2527            .and_then(|p| p.as_column_fill())
2528    }
2529
2530    // Method for getting column-rule-width property
2531    pub fn get_column_rule_width<'a>(
2532        &'a self,
2533        node_data: &'a NodeData,
2534        node_id: &NodeId,
2535        node_state: &StyledNodeState,
2536    ) -> Option<&'a ColumnRuleWidthValue> {
2537        self.get_property(
2538            node_data,
2539            node_id,
2540            node_state,
2541            &CssPropertyType::ColumnRuleWidth,
2542        )
2543        .and_then(|p| p.as_column_rule_width())
2544    }
2545
2546    // Method for getting column-rule-style property
2547    pub fn get_column_rule_style<'a>(
2548        &'a self,
2549        node_data: &'a NodeData,
2550        node_id: &NodeId,
2551        node_state: &StyledNodeState,
2552    ) -> Option<&'a ColumnRuleStyleValue> {
2553        self.get_property(
2554            node_data,
2555            node_id,
2556            node_state,
2557            &CssPropertyType::ColumnRuleStyle,
2558        )
2559        .and_then(|p| p.as_column_rule_style())
2560    }
2561
2562    // Method for getting column-rule-color property
2563    pub fn get_column_rule_color<'a>(
2564        &'a self,
2565        node_data: &'a NodeData,
2566        node_id: &NodeId,
2567        node_state: &StyledNodeState,
2568    ) -> Option<&'a ColumnRuleColorValue> {
2569        self.get_property(
2570            node_data,
2571            node_id,
2572            node_state,
2573            &CssPropertyType::ColumnRuleColor,
2574        )
2575        .and_then(|p| p.as_column_rule_color())
2576    }
2577
2578    // Method for getting flow-into property
2579    pub fn get_flow_into<'a>(
2580        &'a self,
2581        node_data: &'a NodeData,
2582        node_id: &NodeId,
2583        node_state: &StyledNodeState,
2584    ) -> Option<&'a FlowIntoValue> {
2585        self.get_property(node_data, node_id, node_state, &CssPropertyType::FlowInto)
2586            .and_then(|p| p.as_flow_into())
2587    }
2588
2589    // Method for getting flow-from property
2590    pub fn get_flow_from<'a>(
2591        &'a self,
2592        node_data: &'a NodeData,
2593        node_id: &NodeId,
2594        node_state: &StyledNodeState,
2595    ) -> Option<&'a FlowFromValue> {
2596        self.get_property(node_data, node_id, node_state, &CssPropertyType::FlowFrom)
2597            .and_then(|p| p.as_flow_from())
2598    }
2599
2600    // Method for getting shape-margin property
2601    pub fn get_shape_margin<'a>(
2602        &'a self,
2603        node_data: &'a NodeData,
2604        node_id: &NodeId,
2605        node_state: &StyledNodeState,
2606    ) -> Option<&'a ShapeMarginValue> {
2607        self.get_property(
2608            node_data,
2609            node_id,
2610            node_state,
2611            &CssPropertyType::ShapeMargin,
2612        )
2613        .and_then(|p| p.as_shape_margin())
2614    }
2615
2616    // Method for getting shape-image-threshold property
2617    pub fn get_shape_image_threshold<'a>(
2618        &'a self,
2619        node_data: &'a NodeData,
2620        node_id: &NodeId,
2621        node_state: &StyledNodeState,
2622    ) -> Option<&'a ShapeImageThresholdValue> {
2623        self.get_property(
2624            node_data,
2625            node_id,
2626            node_state,
2627            &CssPropertyType::ShapeImageThreshold,
2628        )
2629        .and_then(|p| p.as_shape_image_threshold())
2630    }
2631
2632    // Method for getting content property
2633    pub fn get_content<'a>(
2634        &'a self,
2635        node_data: &'a NodeData,
2636        node_id: &NodeId,
2637        node_state: &StyledNodeState,
2638    ) -> Option<&'a ContentValue> {
2639        self.get_property(node_data, node_id, node_state, &CssPropertyType::Content)
2640            .and_then(|p| p.as_content())
2641    }
2642
2643    // Method for getting counter-reset property
2644    pub fn get_counter_reset<'a>(
2645        &'a self,
2646        node_data: &'a NodeData,
2647        node_id: &NodeId,
2648        node_state: &StyledNodeState,
2649    ) -> Option<&'a CounterResetValue> {
2650        self.get_property(
2651            node_data,
2652            node_id,
2653            node_state,
2654            &CssPropertyType::CounterReset,
2655        )
2656        .and_then(|p| p.as_counter_reset())
2657    }
2658
2659    // Method for getting counter-increment property
2660    pub fn get_counter_increment<'a>(
2661        &'a self,
2662        node_data: &'a NodeData,
2663        node_id: &NodeId,
2664        node_state: &StyledNodeState,
2665    ) -> Option<&'a CounterIncrementValue> {
2666        self.get_property(
2667            node_data,
2668            node_id,
2669            node_state,
2670            &CssPropertyType::CounterIncrement,
2671        )
2672        .and_then(|p| p.as_counter_increment())
2673    }
2674
2675    // Method for getting string-set property
2676    pub fn get_string_set<'a>(
2677        &'a self,
2678        node_data: &'a NodeData,
2679        node_id: &NodeId,
2680        node_state: &StyledNodeState,
2681    ) -> Option<&'a StringSetValue> {
2682        self.get_property(node_data, node_id, node_state, &CssPropertyType::StringSet)
2683            .and_then(|p| p.as_string_set())
2684    }
2685    pub fn get_text_align<'a>(
2686        &'a self,
2687        node_data: &'a NodeData,
2688        node_id: &NodeId,
2689        node_state: &StyledNodeState,
2690    ) -> Option<&'a StyleTextAlignValue> {
2691        self.get_property(node_data, node_id, node_state, &CssPropertyType::TextAlign)
2692            .and_then(|p| p.as_text_align())
2693    }
2694    pub fn get_user_select<'a>(
2695        &'a self,
2696        node_data: &'a NodeData,
2697        node_id: &NodeId,
2698        node_state: &StyledNodeState,
2699    ) -> Option<&'a StyleUserSelectValue> {
2700        self.get_property(node_data, node_id, node_state, &CssPropertyType::UserSelect)
2701            .and_then(|p| p.as_user_select())
2702    }
2703    pub fn get_text_decoration<'a>(
2704        &'a self,
2705        node_data: &'a NodeData,
2706        node_id: &NodeId,
2707        node_state: &StyledNodeState,
2708    ) -> Option<&'a StyleTextDecorationValue> {
2709        self.get_property(
2710            node_data,
2711            node_id,
2712            node_state,
2713            &CssPropertyType::TextDecoration,
2714        )
2715        .and_then(|p| p.as_text_decoration())
2716    }
2717    pub fn get_vertical_align<'a>(
2718        &'a self,
2719        node_data: &'a NodeData,
2720        node_id: &NodeId,
2721        node_state: &StyledNodeState,
2722    ) -> Option<&'a StyleVerticalAlignValue> {
2723        self.get_property(
2724            node_data,
2725            node_id,
2726            node_state,
2727            &CssPropertyType::VerticalAlign,
2728        )
2729        .and_then(|p| p.as_vertical_align())
2730    }
2731    pub fn get_line_height<'a>(
2732        &'a self,
2733        node_data: &'a NodeData,
2734        node_id: &NodeId,
2735        node_state: &StyledNodeState,
2736    ) -> Option<&'a StyleLineHeightValue> {
2737        self.get_property(node_data, node_id, node_state, &CssPropertyType::LineHeight)
2738            .and_then(|p| p.as_line_height())
2739    }
2740    pub fn get_letter_spacing<'a>(
2741        &'a self,
2742        node_data: &'a NodeData,
2743        node_id: &NodeId,
2744        node_state: &StyledNodeState,
2745    ) -> Option<&'a StyleLetterSpacingValue> {
2746        self.get_property(
2747            node_data,
2748            node_id,
2749            node_state,
2750            &CssPropertyType::LetterSpacing,
2751        )
2752        .and_then(|p| p.as_letter_spacing())
2753    }
2754    pub fn get_word_spacing<'a>(
2755        &'a self,
2756        node_data: &'a NodeData,
2757        node_id: &NodeId,
2758        node_state: &StyledNodeState,
2759    ) -> Option<&'a StyleWordSpacingValue> {
2760        self.get_property(
2761            node_data,
2762            node_id,
2763            node_state,
2764            &CssPropertyType::WordSpacing,
2765        )
2766        .and_then(|p| p.as_word_spacing())
2767    }
2768    pub fn get_tab_width<'a>(
2769        &'a self,
2770        node_data: &'a NodeData,
2771        node_id: &NodeId,
2772        node_state: &StyledNodeState,
2773    ) -> Option<&'a StyleTabWidthValue> {
2774        self.get_property(node_data, node_id, node_state, &CssPropertyType::TabWidth)
2775            .and_then(|p| p.as_tab_width())
2776    }
2777    pub fn get_cursor<'a>(
2778        &'a self,
2779        node_data: &'a NodeData,
2780        node_id: &NodeId,
2781        node_state: &StyledNodeState,
2782    ) -> Option<&'a StyleCursorValue> {
2783        self.get_property(node_data, node_id, node_state, &CssPropertyType::Cursor)
2784            .and_then(|p| p.as_cursor())
2785    }
2786    pub fn get_box_shadow_left<'a>(
2787        &'a self,
2788        node_data: &'a NodeData,
2789        node_id: &NodeId,
2790        node_state: &StyledNodeState,
2791    ) -> Option<&'a StyleBoxShadowValue> {
2792        self.get_property(
2793            node_data,
2794            node_id,
2795            node_state,
2796            &CssPropertyType::BoxShadowLeft,
2797        )
2798        .and_then(|p| p.as_box_shadow_left())
2799    }
2800    pub fn get_box_shadow_right<'a>(
2801        &'a self,
2802        node_data: &'a NodeData,
2803        node_id: &NodeId,
2804        node_state: &StyledNodeState,
2805    ) -> Option<&'a StyleBoxShadowValue> {
2806        self.get_property(
2807            node_data,
2808            node_id,
2809            node_state,
2810            &CssPropertyType::BoxShadowRight,
2811        )
2812        .and_then(|p| p.as_box_shadow_right())
2813    }
2814    pub fn get_box_shadow_top<'a>(
2815        &'a self,
2816        node_data: &'a NodeData,
2817        node_id: &NodeId,
2818        node_state: &StyledNodeState,
2819    ) -> Option<&'a StyleBoxShadowValue> {
2820        self.get_property(
2821            node_data,
2822            node_id,
2823            node_state,
2824            &CssPropertyType::BoxShadowTop,
2825        )
2826        .and_then(|p| p.as_box_shadow_top())
2827    }
2828    pub fn get_box_shadow_bottom<'a>(
2829        &'a self,
2830        node_data: &'a NodeData,
2831        node_id: &NodeId,
2832        node_state: &StyledNodeState,
2833    ) -> Option<&'a StyleBoxShadowValue> {
2834        self.get_property(
2835            node_data,
2836            node_id,
2837            node_state,
2838            &CssPropertyType::BoxShadowBottom,
2839        )
2840        .and_then(|p| p.as_box_shadow_bottom())
2841    }
2842    pub fn get_border_top_color<'a>(
2843        &'a self,
2844        node_data: &'a NodeData,
2845        node_id: &NodeId,
2846        node_state: &StyledNodeState,
2847    ) -> Option<&'a StyleBorderTopColorValue> {
2848        self.get_property(
2849            node_data,
2850            node_id,
2851            node_state,
2852            &CssPropertyType::BorderTopColor,
2853        )
2854        .and_then(|p| p.as_border_top_color())
2855    }
2856    pub fn get_border_left_color<'a>(
2857        &'a self,
2858        node_data: &'a NodeData,
2859        node_id: &NodeId,
2860        node_state: &StyledNodeState,
2861    ) -> Option<&'a StyleBorderLeftColorValue> {
2862        self.get_property(
2863            node_data,
2864            node_id,
2865            node_state,
2866            &CssPropertyType::BorderLeftColor,
2867        )
2868        .and_then(|p| p.as_border_left_color())
2869    }
2870    pub fn get_border_right_color<'a>(
2871        &'a self,
2872        node_data: &'a NodeData,
2873        node_id: &NodeId,
2874        node_state: &StyledNodeState,
2875    ) -> Option<&'a StyleBorderRightColorValue> {
2876        self.get_property(
2877            node_data,
2878            node_id,
2879            node_state,
2880            &CssPropertyType::BorderRightColor,
2881        )
2882        .and_then(|p| p.as_border_right_color())
2883    }
2884    pub fn get_border_bottom_color<'a>(
2885        &'a self,
2886        node_data: &'a NodeData,
2887        node_id: &NodeId,
2888        node_state: &StyledNodeState,
2889    ) -> Option<&'a StyleBorderBottomColorValue> {
2890        self.get_property(
2891            node_data,
2892            node_id,
2893            node_state,
2894            &CssPropertyType::BorderBottomColor,
2895        )
2896        .and_then(|p| p.as_border_bottom_color())
2897    }
2898    pub fn get_border_top_style<'a>(
2899        &'a self,
2900        node_data: &'a NodeData,
2901        node_id: &NodeId,
2902        node_state: &StyledNodeState,
2903    ) -> Option<&'a StyleBorderTopStyleValue> {
2904        self.get_property(
2905            node_data,
2906            node_id,
2907            node_state,
2908            &CssPropertyType::BorderTopStyle,
2909        )
2910        .and_then(|p| p.as_border_top_style())
2911    }
2912    pub fn get_border_left_style<'a>(
2913        &'a self,
2914        node_data: &'a NodeData,
2915        node_id: &NodeId,
2916        node_state: &StyledNodeState,
2917    ) -> Option<&'a StyleBorderLeftStyleValue> {
2918        self.get_property(
2919            node_data,
2920            node_id,
2921            node_state,
2922            &CssPropertyType::BorderLeftStyle,
2923        )
2924        .and_then(|p| p.as_border_left_style())
2925    }
2926    pub fn get_border_right_style<'a>(
2927        &'a self,
2928        node_data: &'a NodeData,
2929        node_id: &NodeId,
2930        node_state: &StyledNodeState,
2931    ) -> Option<&'a StyleBorderRightStyleValue> {
2932        self.get_property(
2933            node_data,
2934            node_id,
2935            node_state,
2936            &CssPropertyType::BorderRightStyle,
2937        )
2938        .and_then(|p| p.as_border_right_style())
2939    }
2940    pub fn get_border_bottom_style<'a>(
2941        &'a self,
2942        node_data: &'a NodeData,
2943        node_id: &NodeId,
2944        node_state: &StyledNodeState,
2945    ) -> Option<&'a StyleBorderBottomStyleValue> {
2946        self.get_property(
2947            node_data,
2948            node_id,
2949            node_state,
2950            &CssPropertyType::BorderBottomStyle,
2951        )
2952        .and_then(|p| p.as_border_bottom_style())
2953    }
2954    pub fn get_border_top_left_radius<'a>(
2955        &'a self,
2956        node_data: &'a NodeData,
2957        node_id: &NodeId,
2958        node_state: &StyledNodeState,
2959    ) -> Option<&'a StyleBorderTopLeftRadiusValue> {
2960        self.get_property(
2961            node_data,
2962            node_id,
2963            node_state,
2964            &CssPropertyType::BorderTopLeftRadius,
2965        )
2966        .and_then(|p| p.as_border_top_left_radius())
2967    }
2968    pub fn get_border_top_right_radius<'a>(
2969        &'a self,
2970        node_data: &'a NodeData,
2971        node_id: &NodeId,
2972        node_state: &StyledNodeState,
2973    ) -> Option<&'a StyleBorderTopRightRadiusValue> {
2974        self.get_property(
2975            node_data,
2976            node_id,
2977            node_state,
2978            &CssPropertyType::BorderTopRightRadius,
2979        )
2980        .and_then(|p| p.as_border_top_right_radius())
2981    }
2982    pub fn get_border_bottom_left_radius<'a>(
2983        &'a self,
2984        node_data: &'a NodeData,
2985        node_id: &NodeId,
2986        node_state: &StyledNodeState,
2987    ) -> Option<&'a StyleBorderBottomLeftRadiusValue> {
2988        self.get_property(
2989            node_data,
2990            node_id,
2991            node_state,
2992            &CssPropertyType::BorderBottomLeftRadius,
2993        )
2994        .and_then(|p| p.as_border_bottom_left_radius())
2995    }
2996    pub fn get_border_bottom_right_radius<'a>(
2997        &'a self,
2998        node_data: &'a NodeData,
2999        node_id: &NodeId,
3000        node_state: &StyledNodeState,
3001    ) -> Option<&'a StyleBorderBottomRightRadiusValue> {
3002        self.get_property(
3003            node_data,
3004            node_id,
3005            node_state,
3006            &CssPropertyType::BorderBottomRightRadius,
3007        )
3008        .and_then(|p| p.as_border_bottom_right_radius())
3009    }
3010    pub fn get_opacity<'a>(
3011        &'a self,
3012        node_data: &'a NodeData,
3013        node_id: &NodeId,
3014        node_state: &StyledNodeState,
3015    ) -> Option<&'a StyleOpacityValue> {
3016        self.get_property(node_data, node_id, node_state, &CssPropertyType::Opacity)
3017            .and_then(|p| p.as_opacity())
3018    }
3019    pub fn get_transform<'a>(
3020        &'a self,
3021        node_data: &'a NodeData,
3022        node_id: &NodeId,
3023        node_state: &StyledNodeState,
3024    ) -> Option<&'a StyleTransformVecValue> {
3025        self.get_property(node_data, node_id, node_state, &CssPropertyType::Transform)
3026            .and_then(|p| p.as_transform())
3027    }
3028    pub fn get_transform_origin<'a>(
3029        &'a self,
3030        node_data: &'a NodeData,
3031        node_id: &NodeId,
3032        node_state: &StyledNodeState,
3033    ) -> Option<&'a StyleTransformOriginValue> {
3034        self.get_property(
3035            node_data,
3036            node_id,
3037            node_state,
3038            &CssPropertyType::TransformOrigin,
3039        )
3040        .and_then(|p| p.as_transform_origin())
3041    }
3042    pub fn get_perspective_origin<'a>(
3043        &'a self,
3044        node_data: &'a NodeData,
3045        node_id: &NodeId,
3046        node_state: &StyledNodeState,
3047    ) -> Option<&'a StylePerspectiveOriginValue> {
3048        self.get_property(
3049            node_data,
3050            node_id,
3051            node_state,
3052            &CssPropertyType::PerspectiveOrigin,
3053        )
3054        .and_then(|p| p.as_perspective_origin())
3055    }
3056    pub fn get_backface_visibility<'a>(
3057        &'a self,
3058        node_data: &'a NodeData,
3059        node_id: &NodeId,
3060        node_state: &StyledNodeState,
3061    ) -> Option<&'a StyleBackfaceVisibilityValue> {
3062        self.get_property(
3063            node_data,
3064            node_id,
3065            node_state,
3066            &CssPropertyType::BackfaceVisibility,
3067        )
3068        .and_then(|p| p.as_backface_visibility())
3069    }
3070    pub fn get_display<'a>(
3071        &'a self,
3072        node_data: &'a NodeData,
3073        node_id: &NodeId,
3074        node_state: &StyledNodeState,
3075    ) -> Option<&'a LayoutDisplayValue> {
3076        self.get_property(node_data, node_id, node_state, &CssPropertyType::Display)
3077            .and_then(|p| p.as_display())
3078    }
3079    pub fn get_float<'a>(
3080        &'a self,
3081        node_data: &'a NodeData,
3082        node_id: &NodeId,
3083        node_state: &StyledNodeState,
3084    ) -> Option<&'a LayoutFloatValue> {
3085        self.get_property(node_data, node_id, node_state, &CssPropertyType::Float)
3086            .and_then(|p| p.as_float())
3087    }
3088    pub fn get_box_sizing<'a>(
3089        &'a self,
3090        node_data: &'a NodeData,
3091        node_id: &NodeId,
3092        node_state: &StyledNodeState,
3093    ) -> Option<&'a LayoutBoxSizingValue> {
3094        self.get_property(node_data, node_id, node_state, &CssPropertyType::BoxSizing)
3095            .and_then(|p| p.as_box_sizing())
3096    }
3097    pub fn get_width<'a>(
3098        &'a self,
3099        node_data: &'a NodeData,
3100        node_id: &NodeId,
3101        node_state: &StyledNodeState,
3102    ) -> Option<&'a LayoutWidthValue> {
3103        self.get_property(node_data, node_id, node_state, &CssPropertyType::Width)
3104            .and_then(|p| p.as_width())
3105    }
3106    pub fn get_height<'a>(
3107        &'a self,
3108        node_data: &'a NodeData,
3109        node_id: &NodeId,
3110        node_state: &StyledNodeState,
3111    ) -> Option<&'a LayoutHeightValue> {
3112        self.get_property(node_data, node_id, node_state, &CssPropertyType::Height)
3113            .and_then(|p| p.as_height())
3114    }
3115    pub fn get_min_width<'a>(
3116        &'a self,
3117        node_data: &'a NodeData,
3118        node_id: &NodeId,
3119        node_state: &StyledNodeState,
3120    ) -> Option<&'a LayoutMinWidthValue> {
3121        self.get_property(node_data, node_id, node_state, &CssPropertyType::MinWidth)
3122            .and_then(|p| p.as_min_width())
3123    }
3124    pub fn get_min_height<'a>(
3125        &'a self,
3126        node_data: &'a NodeData,
3127        node_id: &NodeId,
3128        node_state: &StyledNodeState,
3129    ) -> Option<&'a LayoutMinHeightValue> {
3130        self.get_property(node_data, node_id, node_state, &CssPropertyType::MinHeight)
3131            .and_then(|p| p.as_min_height())
3132    }
3133    pub fn get_max_width<'a>(
3134        &'a self,
3135        node_data: &'a NodeData,
3136        node_id: &NodeId,
3137        node_state: &StyledNodeState,
3138    ) -> Option<&'a LayoutMaxWidthValue> {
3139        self.get_property(node_data, node_id, node_state, &CssPropertyType::MaxWidth)
3140            .and_then(|p| p.as_max_width())
3141    }
3142    pub fn get_max_height<'a>(
3143        &'a self,
3144        node_data: &'a NodeData,
3145        node_id: &NodeId,
3146        node_state: &StyledNodeState,
3147    ) -> Option<&'a LayoutMaxHeightValue> {
3148        self.get_property(node_data, node_id, node_state, &CssPropertyType::MaxHeight)
3149            .and_then(|p| p.as_max_height())
3150    }
3151    pub fn get_position<'a>(
3152        &'a self,
3153        node_data: &'a NodeData,
3154        node_id: &NodeId,
3155        node_state: &StyledNodeState,
3156    ) -> Option<&'a LayoutPositionValue> {
3157        self.get_property(node_data, node_id, node_state, &CssPropertyType::Position)
3158            .and_then(|p| p.as_position())
3159    }
3160    pub fn get_top<'a>(
3161        &'a self,
3162        node_data: &'a NodeData,
3163        node_id: &NodeId,
3164        node_state: &StyledNodeState,
3165    ) -> Option<&'a LayoutTopValue> {
3166        self.get_property(node_data, node_id, node_state, &CssPropertyType::Top)
3167            .and_then(|p| p.as_top())
3168    }
3169    pub fn get_bottom<'a>(
3170        &'a self,
3171        node_data: &'a NodeData,
3172        node_id: &NodeId,
3173        node_state: &StyledNodeState,
3174    ) -> Option<&'a LayoutInsetBottomValue> {
3175        self.get_property(node_data, node_id, node_state, &CssPropertyType::Bottom)
3176            .and_then(|p| p.as_bottom())
3177    }
3178    pub fn get_right<'a>(
3179        &'a self,
3180        node_data: &'a NodeData,
3181        node_id: &NodeId,
3182        node_state: &StyledNodeState,
3183    ) -> Option<&'a LayoutRightValue> {
3184        self.get_property(node_data, node_id, node_state, &CssPropertyType::Right)
3185            .and_then(|p| p.as_right())
3186    }
3187    pub fn get_left<'a>(
3188        &'a self,
3189        node_data: &'a NodeData,
3190        node_id: &NodeId,
3191        node_state: &StyledNodeState,
3192    ) -> Option<&'a LayoutLeftValue> {
3193        self.get_property(node_data, node_id, node_state, &CssPropertyType::Left)
3194            .and_then(|p| p.as_left())
3195    }
3196    pub fn get_padding_top<'a>(
3197        &'a self,
3198        node_data: &'a NodeData,
3199        node_id: &NodeId,
3200        node_state: &StyledNodeState,
3201    ) -> Option<&'a LayoutPaddingTopValue> {
3202        self.get_property(node_data, node_id, node_state, &CssPropertyType::PaddingTop)
3203            .and_then(|p| p.as_padding_top())
3204    }
3205    pub fn get_padding_bottom<'a>(
3206        &'a self,
3207        node_data: &'a NodeData,
3208        node_id: &NodeId,
3209        node_state: &StyledNodeState,
3210    ) -> Option<&'a LayoutPaddingBottomValue> {
3211        self.get_property(
3212            node_data,
3213            node_id,
3214            node_state,
3215            &CssPropertyType::PaddingBottom,
3216        )
3217        .and_then(|p| p.as_padding_bottom())
3218    }
3219    pub fn get_padding_left<'a>(
3220        &'a self,
3221        node_data: &'a NodeData,
3222        node_id: &NodeId,
3223        node_state: &StyledNodeState,
3224    ) -> Option<&'a LayoutPaddingLeftValue> {
3225        self.get_property(
3226            node_data,
3227            node_id,
3228            node_state,
3229            &CssPropertyType::PaddingLeft,
3230        )
3231        .and_then(|p| p.as_padding_left())
3232    }
3233    pub fn get_padding_right<'a>(
3234        &'a self,
3235        node_data: &'a NodeData,
3236        node_id: &NodeId,
3237        node_state: &StyledNodeState,
3238    ) -> Option<&'a LayoutPaddingRightValue> {
3239        self.get_property(
3240            node_data,
3241            node_id,
3242            node_state,
3243            &CssPropertyType::PaddingRight,
3244        )
3245        .and_then(|p| p.as_padding_right())
3246    }
3247    pub fn get_margin_top<'a>(
3248        &'a self,
3249        node_data: &'a NodeData,
3250        node_id: &NodeId,
3251        node_state: &StyledNodeState,
3252    ) -> Option<&'a LayoutMarginTopValue> {
3253        self.get_property(node_data, node_id, node_state, &CssPropertyType::MarginTop)
3254            .and_then(|p| p.as_margin_top())
3255    }
3256    pub fn get_margin_bottom<'a>(
3257        &'a self,
3258        node_data: &'a NodeData,
3259        node_id: &NodeId,
3260        node_state: &StyledNodeState,
3261    ) -> Option<&'a LayoutMarginBottomValue> {
3262        self.get_property(
3263            node_data,
3264            node_id,
3265            node_state,
3266            &CssPropertyType::MarginBottom,
3267        )
3268        .and_then(|p| p.as_margin_bottom())
3269    }
3270    pub fn get_margin_left<'a>(
3271        &'a self,
3272        node_data: &'a NodeData,
3273        node_id: &NodeId,
3274        node_state: &StyledNodeState,
3275    ) -> Option<&'a LayoutMarginLeftValue> {
3276        self.get_property(node_data, node_id, node_state, &CssPropertyType::MarginLeft)
3277            .and_then(|p| p.as_margin_left())
3278    }
3279    pub fn get_margin_right<'a>(
3280        &'a self,
3281        node_data: &'a NodeData,
3282        node_id: &NodeId,
3283        node_state: &StyledNodeState,
3284    ) -> Option<&'a LayoutMarginRightValue> {
3285        self.get_property(
3286            node_data,
3287            node_id,
3288            node_state,
3289            &CssPropertyType::MarginRight,
3290        )
3291        .and_then(|p| p.as_margin_right())
3292    }
3293    pub fn get_border_top_width<'a>(
3294        &'a self,
3295        node_data: &'a NodeData,
3296        node_id: &NodeId,
3297        node_state: &StyledNodeState,
3298    ) -> Option<&'a LayoutBorderTopWidthValue> {
3299        self.get_property(
3300            node_data,
3301            node_id,
3302            node_state,
3303            &CssPropertyType::BorderTopWidth,
3304        )
3305        .and_then(|p| p.as_border_top_width())
3306    }
3307    pub fn get_border_left_width<'a>(
3308        &'a self,
3309        node_data: &'a NodeData,
3310        node_id: &NodeId,
3311        node_state: &StyledNodeState,
3312    ) -> Option<&'a LayoutBorderLeftWidthValue> {
3313        self.get_property(
3314            node_data,
3315            node_id,
3316            node_state,
3317            &CssPropertyType::BorderLeftWidth,
3318        )
3319        .and_then(|p| p.as_border_left_width())
3320    }
3321    pub fn get_border_right_width<'a>(
3322        &'a self,
3323        node_data: &'a NodeData,
3324        node_id: &NodeId,
3325        node_state: &StyledNodeState,
3326    ) -> Option<&'a LayoutBorderRightWidthValue> {
3327        self.get_property(
3328            node_data,
3329            node_id,
3330            node_state,
3331            &CssPropertyType::BorderRightWidth,
3332        )
3333        .and_then(|p| p.as_border_right_width())
3334    }
3335    pub fn get_border_bottom_width<'a>(
3336        &'a self,
3337        node_data: &'a NodeData,
3338        node_id: &NodeId,
3339        node_state: &StyledNodeState,
3340    ) -> Option<&'a LayoutBorderBottomWidthValue> {
3341        self.get_property(
3342            node_data,
3343            node_id,
3344            node_state,
3345            &CssPropertyType::BorderBottomWidth,
3346        )
3347        .and_then(|p| p.as_border_bottom_width())
3348    }
3349    pub fn get_overflow_x<'a>(
3350        &'a self,
3351        node_data: &'a NodeData,
3352        node_id: &NodeId,
3353        node_state: &StyledNodeState,
3354    ) -> Option<&'a LayoutOverflowValue> {
3355        self.get_property(node_data, node_id, node_state, &CssPropertyType::OverflowX)
3356            .and_then(|p| p.as_overflow_x())
3357    }
3358    pub fn get_overflow_y<'a>(
3359        &'a self,
3360        node_data: &'a NodeData,
3361        node_id: &NodeId,
3362        node_state: &StyledNodeState,
3363    ) -> Option<&'a LayoutOverflowValue> {
3364        self.get_property(node_data, node_id, node_state, &CssPropertyType::OverflowY)
3365            .and_then(|p| p.as_overflow_y())
3366    }
3367    pub fn get_flex_direction<'a>(
3368        &'a self,
3369        node_data: &'a NodeData,
3370        node_id: &NodeId,
3371        node_state: &StyledNodeState,
3372    ) -> Option<&'a LayoutFlexDirectionValue> {
3373        self.get_property(
3374            node_data,
3375            node_id,
3376            node_state,
3377            &CssPropertyType::FlexDirection,
3378        )
3379        .and_then(|p| p.as_flex_direction())
3380    }
3381    pub fn get_flex_wrap<'a>(
3382        &'a self,
3383        node_data: &'a NodeData,
3384        node_id: &NodeId,
3385        node_state: &StyledNodeState,
3386    ) -> Option<&'a LayoutFlexWrapValue> {
3387        self.get_property(node_data, node_id, node_state, &CssPropertyType::FlexWrap)
3388            .and_then(|p| p.as_flex_wrap())
3389    }
3390    pub fn get_flex_grow<'a>(
3391        &'a self,
3392        node_data: &'a NodeData,
3393        node_id: &NodeId,
3394        node_state: &StyledNodeState,
3395    ) -> Option<&'a LayoutFlexGrowValue> {
3396        self.get_property(node_data, node_id, node_state, &CssPropertyType::FlexGrow)
3397            .and_then(|p| p.as_flex_grow())
3398    }
3399    pub fn get_flex_shrink<'a>(
3400        &'a self,
3401        node_data: &'a NodeData,
3402        node_id: &NodeId,
3403        node_state: &StyledNodeState,
3404    ) -> Option<&'a LayoutFlexShrinkValue> {
3405        self.get_property(node_data, node_id, node_state, &CssPropertyType::FlexShrink)
3406            .and_then(|p| p.as_flex_shrink())
3407    }
3408    pub fn get_justify_content<'a>(
3409        &'a self,
3410        node_data: &'a NodeData,
3411        node_id: &NodeId,
3412        node_state: &StyledNodeState,
3413    ) -> Option<&'a LayoutJustifyContentValue> {
3414        self.get_property(
3415            node_data,
3416            node_id,
3417            node_state,
3418            &CssPropertyType::JustifyContent,
3419        )
3420        .and_then(|p| p.as_justify_content())
3421    }
3422    pub fn get_align_items<'a>(
3423        &'a self,
3424        node_data: &'a NodeData,
3425        node_id: &NodeId,
3426        node_state: &StyledNodeState,
3427    ) -> Option<&'a LayoutAlignItemsValue> {
3428        self.get_property(node_data, node_id, node_state, &CssPropertyType::AlignItems)
3429            .and_then(|p| p.as_align_items())
3430    }
3431    pub fn get_align_content<'a>(
3432        &'a self,
3433        node_data: &'a NodeData,
3434        node_id: &NodeId,
3435        node_state: &StyledNodeState,
3436    ) -> Option<&'a LayoutAlignContentValue> {
3437        self.get_property(
3438            node_data,
3439            node_id,
3440            node_state,
3441            &CssPropertyType::AlignContent,
3442        )
3443        .and_then(|p| p.as_align_content())
3444    }
3445    pub fn get_mix_blend_mode<'a>(
3446        &'a self,
3447        node_data: &'a NodeData,
3448        node_id: &NodeId,
3449        node_state: &StyledNodeState,
3450    ) -> Option<&'a StyleMixBlendModeValue> {
3451        self.get_property(
3452            node_data,
3453            node_id,
3454            node_state,
3455            &CssPropertyType::MixBlendMode,
3456        )
3457        .and_then(|p| p.as_mix_blend_mode())
3458    }
3459    pub fn get_filter<'a>(
3460        &'a self,
3461        node_data: &'a NodeData,
3462        node_id: &NodeId,
3463        node_state: &StyledNodeState,
3464    ) -> Option<&'a StyleFilterVecValue> {
3465        self.get_property(node_data, node_id, node_state, &CssPropertyType::Filter)
3466            .and_then(|p| p.as_filter())
3467    }
3468    pub fn get_backdrop_filter<'a>(
3469        &'a self,
3470        node_data: &'a NodeData,
3471        node_id: &NodeId,
3472        node_state: &StyledNodeState,
3473    ) -> Option<&'a StyleFilterVecValue> {
3474        self.get_property(node_data, node_id, node_state, &CssPropertyType::Filter)
3475            .and_then(|p| p.as_backdrop_filter())
3476    }
3477    pub fn get_text_shadow<'a>(
3478        &'a self,
3479        node_data: &'a NodeData,
3480        node_id: &NodeId,
3481        node_state: &StyledNodeState,
3482    ) -> Option<&'a StyleBoxShadowValue> {
3483        self.get_property(node_data, node_id, node_state, &CssPropertyType::TextShadow)
3484            .and_then(|p| p.as_text_shadow())
3485    }
3486    pub fn get_list_style_type<'a>(
3487        &'a self,
3488        node_data: &'a NodeData,
3489        node_id: &NodeId,
3490        node_state: &StyledNodeState,
3491    ) -> Option<&'a StyleListStyleTypeValue> {
3492        self.get_property(
3493            node_data,
3494            node_id,
3495            node_state,
3496            &CssPropertyType::ListStyleType,
3497        )
3498        .and_then(|p| p.as_list_style_type())
3499    }
3500    pub fn get_list_style_position<'a>(
3501        &'a self,
3502        node_data: &'a NodeData,
3503        node_id: &NodeId,
3504        node_state: &StyledNodeState,
3505    ) -> Option<&'a StyleListStylePositionValue> {
3506        self.get_property(
3507            node_data,
3508            node_id,
3509            node_state,
3510            &CssPropertyType::ListStylePosition,
3511        )
3512        .and_then(|p| p.as_list_style_position())
3513    }
3514    pub fn get_table_layout<'a>(
3515        &'a self,
3516        node_data: &'a NodeData,
3517        node_id: &NodeId,
3518        node_state: &StyledNodeState,
3519    ) -> Option<&'a LayoutTableLayoutValue> {
3520        self.get_property(
3521            node_data,
3522            node_id,
3523            node_state,
3524            &CssPropertyType::TableLayout,
3525        )
3526        .and_then(|p| p.as_table_layout())
3527    }
3528    pub fn get_border_collapse<'a>(
3529        &'a self,
3530        node_data: &'a NodeData,
3531        node_id: &NodeId,
3532        node_state: &StyledNodeState,
3533    ) -> Option<&'a StyleBorderCollapseValue> {
3534        self.get_property(
3535            node_data,
3536            node_id,
3537            node_state,
3538            &CssPropertyType::BorderCollapse,
3539        )
3540        .and_then(|p| p.as_border_collapse())
3541    }
3542    pub fn get_border_spacing<'a>(
3543        &'a self,
3544        node_data: &'a NodeData,
3545        node_id: &NodeId,
3546        node_state: &StyledNodeState,
3547    ) -> Option<&'a LayoutBorderSpacingValue> {
3548        self.get_property(
3549            node_data,
3550            node_id,
3551            node_state,
3552            &CssPropertyType::BorderSpacing,
3553        )
3554        .and_then(|p| p.as_border_spacing())
3555    }
3556    pub fn get_caption_side<'a>(
3557        &'a self,
3558        node_data: &'a NodeData,
3559        node_id: &NodeId,
3560        node_state: &StyledNodeState,
3561    ) -> Option<&'a StyleCaptionSideValue> {
3562        self.get_property(
3563            node_data,
3564            node_id,
3565            node_state,
3566            &CssPropertyType::CaptionSide,
3567        )
3568        .and_then(|p| p.as_caption_side())
3569    }
3570    pub fn get_empty_cells<'a>(
3571        &'a self,
3572        node_data: &'a NodeData,
3573        node_id: &NodeId,
3574        node_state: &StyledNodeState,
3575    ) -> Option<&'a StyleEmptyCellsValue> {
3576        self.get_property(node_data, node_id, node_state, &CssPropertyType::EmptyCells)
3577            .and_then(|p| p.as_empty_cells())
3578    }
3579
3580    // Width calculation methods
3581    pub fn calc_width(
3582        &self,
3583        node_data: &NodeData,
3584        node_id: &NodeId,
3585        styled_node_state: &StyledNodeState,
3586        reference_width: f32,
3587    ) -> f32 {
3588        self.get_width(node_data, node_id, styled_node_state)
3589            .and_then(|w| match w.get_property()? {
3590                LayoutWidth::Px(px) => Some(px.to_pixels_internal(
3591                    reference_width,
3592                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3593                )),
3594                _ => Some(0.0), // min-content/max-content not resolved here
3595            })
3596            .unwrap_or(0.0)
3597    }
3598
3599    pub fn calc_min_width(
3600        &self,
3601        node_data: &NodeData,
3602        node_id: &NodeId,
3603        styled_node_state: &StyledNodeState,
3604        reference_width: f32,
3605    ) -> f32 {
3606        self.get_min_width(node_data, node_id, styled_node_state)
3607            .and_then(|w| {
3608                Some(w.get_property()?.inner.to_pixels_internal(
3609                    reference_width,
3610                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3611                ))
3612            })
3613            .unwrap_or(0.0)
3614    }
3615
3616    pub fn calc_max_width(
3617        &self,
3618        node_data: &NodeData,
3619        node_id: &NodeId,
3620        styled_node_state: &StyledNodeState,
3621        reference_width: f32,
3622    ) -> Option<f32> {
3623        self.get_max_width(node_data, node_id, styled_node_state)
3624            .and_then(|w| {
3625                Some(w.get_property()?.inner.to_pixels_internal(
3626                    reference_width,
3627                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3628                ))
3629            })
3630    }
3631
3632    // Height calculation methods
3633    pub fn calc_height(
3634        &self,
3635        node_data: &NodeData,
3636        node_id: &NodeId,
3637        styled_node_state: &StyledNodeState,
3638        reference_height: f32,
3639    ) -> f32 {
3640        self.get_height(node_data, node_id, styled_node_state)
3641            .and_then(|h| match h.get_property()? {
3642                LayoutHeight::Px(px) => Some(px.to_pixels_internal(
3643                    reference_height,
3644                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3645                )),
3646                _ => Some(0.0), // min-content/max-content not resolved here
3647            })
3648            .unwrap_or(0.0)
3649    }
3650
3651    pub fn calc_min_height(
3652        &self,
3653        node_data: &NodeData,
3654        node_id: &NodeId,
3655        styled_node_state: &StyledNodeState,
3656        reference_height: f32,
3657    ) -> f32 {
3658        self.get_min_height(node_data, node_id, styled_node_state)
3659            .and_then(|h| {
3660                Some(h.get_property()?.inner.to_pixels_internal(
3661                    reference_height,
3662                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3663                ))
3664            })
3665            .unwrap_or(0.0)
3666    }
3667
3668    pub fn calc_max_height(
3669        &self,
3670        node_data: &NodeData,
3671        node_id: &NodeId,
3672        styled_node_state: &StyledNodeState,
3673        reference_height: f32,
3674    ) -> Option<f32> {
3675        self.get_max_height(node_data, node_id, styled_node_state)
3676            .and_then(|h| {
3677                Some(h.get_property()?.inner.to_pixels_internal(
3678                    reference_height,
3679                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3680                ))
3681            })
3682    }
3683
3684    // Position calculation methods
3685    pub fn calc_left(
3686        &self,
3687        node_data: &NodeData,
3688        node_id: &NodeId,
3689        styled_node_state: &StyledNodeState,
3690        reference_width: f32,
3691    ) -> Option<f32> {
3692        self.get_left(node_data, node_id, styled_node_state)
3693            .and_then(|l| {
3694                Some(l.get_property()?.inner.to_pixels_internal(
3695                    reference_width,
3696                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3697                ))
3698            })
3699    }
3700
3701    pub fn calc_right(
3702        &self,
3703        node_data: &NodeData,
3704        node_id: &NodeId,
3705        styled_node_state: &StyledNodeState,
3706        reference_width: f32,
3707    ) -> Option<f32> {
3708        self.get_right(node_data, node_id, styled_node_state)
3709            .and_then(|r| {
3710                Some(r.get_property()?.inner.to_pixels_internal(
3711                    reference_width,
3712                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3713                ))
3714            })
3715    }
3716
3717    pub fn calc_top(
3718        &self,
3719        node_data: &NodeData,
3720        node_id: &NodeId,
3721        styled_node_state: &StyledNodeState,
3722        reference_height: f32,
3723    ) -> Option<f32> {
3724        self.get_top(node_data, node_id, styled_node_state)
3725            .and_then(|t| {
3726                Some(t.get_property()?.inner.to_pixels_internal(
3727                    reference_height,
3728                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3729                ))
3730            })
3731    }
3732
3733    pub fn calc_bottom(
3734        &self,
3735        node_data: &NodeData,
3736        node_id: &NodeId,
3737        styled_node_state: &StyledNodeState,
3738        reference_height: f32,
3739    ) -> Option<f32> {
3740        self.get_bottom(node_data, node_id, styled_node_state)
3741            .and_then(|b| {
3742                Some(b.get_property()?.inner.to_pixels_internal(
3743                    reference_height,
3744                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3745                ))
3746            })
3747    }
3748
3749    // Border calculation methods
3750    pub fn calc_border_left_width(
3751        &self,
3752        node_data: &NodeData,
3753        node_id: &NodeId,
3754        styled_node_state: &StyledNodeState,
3755        reference_width: f32,
3756    ) -> f32 {
3757        self.get_border_left_width(node_data, node_id, styled_node_state)
3758            .and_then(|b| {
3759                Some(b.get_property()?.inner.to_pixels_internal(
3760                    reference_width,
3761                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3762                ))
3763            })
3764            .unwrap_or(0.0)
3765    }
3766
3767    pub fn calc_border_right_width(
3768        &self,
3769        node_data: &NodeData,
3770        node_id: &NodeId,
3771        styled_node_state: &StyledNodeState,
3772        reference_width: f32,
3773    ) -> f32 {
3774        self.get_border_right_width(node_data, node_id, styled_node_state)
3775            .and_then(|b| {
3776                Some(b.get_property()?.inner.to_pixels_internal(
3777                    reference_width,
3778                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3779                ))
3780            })
3781            .unwrap_or(0.0)
3782    }
3783
3784    pub fn calc_border_top_width(
3785        &self,
3786        node_data: &NodeData,
3787        node_id: &NodeId,
3788        styled_node_state: &StyledNodeState,
3789        reference_height: f32,
3790    ) -> f32 {
3791        self.get_border_top_width(node_data, node_id, styled_node_state)
3792            .and_then(|b| {
3793                Some(b.get_property()?.inner.to_pixels_internal(
3794                    reference_height,
3795                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3796                ))
3797            })
3798            .unwrap_or(0.0)
3799    }
3800
3801    pub fn calc_border_bottom_width(
3802        &self,
3803        node_data: &NodeData,
3804        node_id: &NodeId,
3805        styled_node_state: &StyledNodeState,
3806        reference_height: f32,
3807    ) -> f32 {
3808        self.get_border_bottom_width(node_data, node_id, styled_node_state)
3809            .and_then(|b| {
3810                Some(b.get_property()?.inner.to_pixels_internal(
3811                    reference_height,
3812                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3813                ))
3814            })
3815            .unwrap_or(0.0)
3816    }
3817
3818    // Padding calculation methods
3819    pub fn calc_padding_left(
3820        &self,
3821        node_data: &NodeData,
3822        node_id: &NodeId,
3823        styled_node_state: &StyledNodeState,
3824        reference_width: f32,
3825    ) -> f32 {
3826        self.get_padding_left(node_data, node_id, styled_node_state)
3827            .and_then(|p| {
3828                Some(p.get_property()?.inner.to_pixels_internal(
3829                    reference_width,
3830                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3831                ))
3832            })
3833            .unwrap_or(0.0)
3834    }
3835
3836    pub fn calc_padding_right(
3837        &self,
3838        node_data: &NodeData,
3839        node_id: &NodeId,
3840        styled_node_state: &StyledNodeState,
3841        reference_width: f32,
3842    ) -> f32 {
3843        self.get_padding_right(node_data, node_id, styled_node_state)
3844            .and_then(|p| {
3845                Some(p.get_property()?.inner.to_pixels_internal(
3846                    reference_width,
3847                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3848                ))
3849            })
3850            .unwrap_or(0.0)
3851    }
3852
3853    pub fn calc_padding_top(
3854        &self,
3855        node_data: &NodeData,
3856        node_id: &NodeId,
3857        styled_node_state: &StyledNodeState,
3858        reference_height: f32,
3859    ) -> f32 {
3860        self.get_padding_top(node_data, node_id, styled_node_state)
3861            .and_then(|p| {
3862                Some(p.get_property()?.inner.to_pixels_internal(
3863                    reference_height,
3864                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3865                ))
3866            })
3867            .unwrap_or(0.0)
3868    }
3869
3870    pub fn calc_padding_bottom(
3871        &self,
3872        node_data: &NodeData,
3873        node_id: &NodeId,
3874        styled_node_state: &StyledNodeState,
3875        reference_height: f32,
3876    ) -> f32 {
3877        self.get_padding_bottom(node_data, node_id, styled_node_state)
3878            .and_then(|p| {
3879                Some(p.get_property()?.inner.to_pixels_internal(
3880                    reference_height,
3881                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3882                ))
3883            })
3884            .unwrap_or(0.0)
3885    }
3886
3887    // Margin calculation methods
3888    pub fn calc_margin_left(
3889        &self,
3890        node_data: &NodeData,
3891        node_id: &NodeId,
3892        styled_node_state: &StyledNodeState,
3893        reference_width: f32,
3894    ) -> f32 {
3895        self.get_margin_left(node_data, node_id, styled_node_state)
3896            .and_then(|m| {
3897                Some(m.get_property()?.inner.to_pixels_internal(
3898                    reference_width,
3899                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3900                ))
3901            })
3902            .unwrap_or(0.0)
3903    }
3904
3905    pub fn calc_margin_right(
3906        &self,
3907        node_data: &NodeData,
3908        node_id: &NodeId,
3909        styled_node_state: &StyledNodeState,
3910        reference_width: f32,
3911    ) -> f32 {
3912        self.get_margin_right(node_data, node_id, styled_node_state)
3913            .and_then(|m| {
3914                Some(m.get_property()?.inner.to_pixels_internal(
3915                    reference_width,
3916                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3917                ))
3918            })
3919            .unwrap_or(0.0)
3920    }
3921
3922    pub fn calc_margin_top(
3923        &self,
3924        node_data: &NodeData,
3925        node_id: &NodeId,
3926        styled_node_state: &StyledNodeState,
3927        reference_height: f32,
3928    ) -> f32 {
3929        self.get_margin_top(node_data, node_id, styled_node_state)
3930            .and_then(|m| {
3931                Some(m.get_property()?.inner.to_pixels_internal(
3932                    reference_height,
3933                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3934                ))
3935            })
3936            .unwrap_or(0.0)
3937    }
3938
3939    pub fn calc_margin_bottom(
3940        &self,
3941        node_data: &NodeData,
3942        node_id: &NodeId,
3943        styled_node_state: &StyledNodeState,
3944        reference_height: f32,
3945    ) -> f32 {
3946        self.get_margin_bottom(node_data, node_id, styled_node_state)
3947            .and_then(|m| {
3948                Some(m.get_property()?.inner.to_pixels_internal(
3949                    reference_height,
3950                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3951                ))
3952            })
3953            .unwrap_or(0.0)
3954    }
3955
3956    /// Helper function to resolve a CSS property value that may depend on another property.
3957    ///
3958    /// This attempts to compute a final pixel value from a property that uses relative units
3959    /// (em, %, etc.) by referencing another property value.
3960    ///
3961    /// # Arguments
3962    /// * `target_property` - The property to resolve (e.g., child's font-size: 2em)
3963    /// * `reference_property` - The property it depends on (e.g., parent's font-size: 16px)
3964    ///
3965    /// # Returns
3966    /// * `Some(CssProperty)` - A new property with absolute pixel values
3967    /// * `None` - If the property can't be resolved (missing data, incompatible types, etc.)
3968    ///
3969    /// # Examples
3970    /// - `resolve_property_dependency(font-size: 2em, font-size: 16px)` → `font-size: 32px`
3971    /// - `resolve_property_dependency(font-size: 150%, font-size: 20px)` → `font-size: 30px`
3972    /// - `resolve_property_dependency(padding: 2em, font-size: 16px)` → `padding: 32px`
3973
3974    /// Resolves CSS cascade keywords (inherit, initial, revert, unset) for a property.
3975    ///
3976    /// According to CSS Cascade spec (https://css-tricks.com/inherit-initial-unset-revert/):
3977    /// - `inherit`: Use the parent's computed value (or initial value if no parent)
3978    /// - `initial`: Use the CSS-defined initial value (default for that property type)
3979    /// - `revert`: Roll back to the user-agent stylesheet value (if any)
3980    /// - `unset`: Behaves as `inherit` for inherited properties, `initial` for non-inherited
3981    ///   properties
3982    ///
3983    /// # Arguments
3984    /// * `property` - The property to resolve
3985    /// * `property_type` - The type of the property
3986    /// * `node_type` - The node type (for UA CSS lookup)
3987    /// * `parent_value` - The parent's computed value (for inheritance)
3988    /// * `ua_value` - The user-agent stylesheet value (for revert)
3989    ///
3990    /// # Returns
3991    /// * `Some(CssProperty)` - The resolved property
3992    /// * `None` - If the keyword doesn't apply or can't be resolved
3993    fn resolve_cascade_keyword(
3994        property: &CssProperty,
3995        property_type: CssPropertyType,
3996        _node_type: &crate::dom::NodeType,
3997        parent_value: Option<&CssProperty>,
3998        ua_value: Option<&'static CssProperty>,
3999    ) -> Option<CssProperty> {
4000        // For now, implement basic inheritance
4001        // Check if this is an inheritable property and return parent value
4002        if property_type.is_inheritable() {
4003            return parent_value.cloned().or_else(|| ua_value.cloned());
4004        } else {
4005            return ua_value.cloned();
4006        }
4007    }
4008
4009    fn resolve_property_dependency(
4010        target_property: &CssProperty,
4011        reference_property: &CssProperty,
4012    ) -> Option<CssProperty> {
4013        use azul_css::{
4014            css::CssPropertyValue,
4015            props::{
4016                basic::{font::StyleFontSize, length::SizeMetric, pixel::PixelValue},
4017                layout::*,
4018                style::{SelectionRadius, StyleLetterSpacing, StyleWordSpacing},
4019            },
4020        };
4021
4022        // Extract PixelValue from various property types (returns owned value)
4023        let get_pixel_value = |prop: &CssProperty| -> Option<PixelValue> {
4024            match prop {
4025                CssProperty::FontSize(val) => val.get_property().map(|v| v.inner),
4026                CssProperty::LetterSpacing(val) => val.get_property().map(|v| v.inner),
4027                CssProperty::WordSpacing(val) => val.get_property().map(|v| v.inner),
4028                CssProperty::PaddingLeft(val) => val.get_property().map(|v| v.inner),
4029                CssProperty::PaddingRight(val) => val.get_property().map(|v| v.inner),
4030                CssProperty::PaddingTop(val) => val.get_property().map(|v| v.inner),
4031                CssProperty::PaddingBottom(val) => val.get_property().map(|v| v.inner),
4032                CssProperty::MarginLeft(val) => val.get_property().map(|v| v.inner),
4033                CssProperty::MarginRight(val) => val.get_property().map(|v| v.inner),
4034                CssProperty::MarginTop(val) => val.get_property().map(|v| v.inner),
4035                CssProperty::MarginBottom(val) => val.get_property().map(|v| v.inner),
4036                CssProperty::MinWidth(val) => val.get_property().map(|v| v.inner),
4037                CssProperty::MinHeight(val) => val.get_property().map(|v| v.inner),
4038                CssProperty::MaxWidth(val) => val.get_property().map(|v| v.inner),
4039                CssProperty::MaxHeight(val) => val.get_property().map(|v| v.inner),
4040                CssProperty::SelectionRadius(val) => val.get_property().map(|v| v.inner),
4041                _ => None,
4042            }
4043        };
4044
4045        let target_pixel_value = get_pixel_value(target_property)?;
4046        let reference_pixel_value = get_pixel_value(reference_property)?;
4047
4048        // Convert reference to absolute pixels first
4049        let reference_px = match reference_pixel_value.metric {
4050            SizeMetric::Px => reference_pixel_value.number.get(),
4051            SizeMetric::Pt => reference_pixel_value.number.get() * 1.333333,
4052            SizeMetric::In => reference_pixel_value.number.get() * 96.0,
4053            SizeMetric::Cm => reference_pixel_value.number.get() * 37.7952755906,
4054            SizeMetric::Mm => reference_pixel_value.number.get() * 3.7795275591,
4055            SizeMetric::Em => return None, // Reference can't be relative
4056            SizeMetric::Rem => return None, // Reference can't be relative
4057            SizeMetric::Percent => return None, // Reference can't be relative
4058            // Reference can't be viewport-relative
4059            SizeMetric::Vw | SizeMetric::Vh | SizeMetric::Vmin | SizeMetric::Vmax => return None,
4060        };
4061
4062        // Resolve target based on reference
4063        let resolved_px = match target_pixel_value.metric {
4064            SizeMetric::Px => target_pixel_value.number.get(),
4065            SizeMetric::Pt => target_pixel_value.number.get() * 1.333333,
4066            SizeMetric::In => target_pixel_value.number.get() * 96.0,
4067            SizeMetric::Cm => target_pixel_value.number.get() * 37.7952755906,
4068            SizeMetric::Mm => target_pixel_value.number.get() * 3.7795275591,
4069            SizeMetric::Em => target_pixel_value.number.get() * reference_px,
4070            // Use reference as root font-size
4071            SizeMetric::Rem => target_pixel_value.number.get() * reference_px,
4072            SizeMetric::Percent => target_pixel_value.number.get() / 100.0 * reference_px,
4073            // Need viewport context
4074            SizeMetric::Vw | SizeMetric::Vh | SizeMetric::Vmin | SizeMetric::Vmax => return None,
4075        };
4076
4077        // Create a new property with the resolved value
4078        let resolved_pixel_value = PixelValue::px(resolved_px);
4079
4080        match target_property {
4081            CssProperty::FontSize(_) => Some(CssProperty::FontSize(CssPropertyValue::Exact(
4082                StyleFontSize {
4083                    inner: resolved_pixel_value,
4084                },
4085            ))),
4086            CssProperty::LetterSpacing(_) => Some(CssProperty::LetterSpacing(
4087                CssPropertyValue::Exact(StyleLetterSpacing {
4088                    inner: resolved_pixel_value,
4089                }),
4090            )),
4091            CssProperty::WordSpacing(_) => Some(CssProperty::WordSpacing(CssPropertyValue::Exact(
4092                StyleWordSpacing {
4093                    inner: resolved_pixel_value,
4094                },
4095            ))),
4096            CssProperty::PaddingLeft(_) => Some(CssProperty::PaddingLeft(CssPropertyValue::Exact(
4097                LayoutPaddingLeft {
4098                    inner: resolved_pixel_value,
4099                },
4100            ))),
4101            CssProperty::PaddingRight(_) => Some(CssProperty::PaddingRight(
4102                CssPropertyValue::Exact(LayoutPaddingRight {
4103                    inner: resolved_pixel_value,
4104                }),
4105            )),
4106            CssProperty::PaddingTop(_) => Some(CssProperty::PaddingTop(CssPropertyValue::Exact(
4107                LayoutPaddingTop {
4108                    inner: resolved_pixel_value,
4109                },
4110            ))),
4111            CssProperty::PaddingBottom(_) => Some(CssProperty::PaddingBottom(
4112                CssPropertyValue::Exact(LayoutPaddingBottom {
4113                    inner: resolved_pixel_value,
4114                }),
4115            )),
4116            CssProperty::MarginLeft(_) => Some(CssProperty::MarginLeft(CssPropertyValue::Exact(
4117                LayoutMarginLeft {
4118                    inner: resolved_pixel_value,
4119                },
4120            ))),
4121            CssProperty::MarginRight(_) => Some(CssProperty::MarginRight(CssPropertyValue::Exact(
4122                LayoutMarginRight {
4123                    inner: resolved_pixel_value,
4124                },
4125            ))),
4126            CssProperty::MarginTop(_) => Some(CssProperty::MarginTop(CssPropertyValue::Exact(
4127                LayoutMarginTop {
4128                    inner: resolved_pixel_value,
4129                },
4130            ))),
4131            CssProperty::MarginBottom(_) => Some(CssProperty::MarginBottom(
4132                CssPropertyValue::Exact(LayoutMarginBottom {
4133                    inner: resolved_pixel_value,
4134                }),
4135            )),
4136            CssProperty::MinWidth(_) => Some(CssProperty::MinWidth(CssPropertyValue::Exact(
4137                LayoutMinWidth {
4138                    inner: resolved_pixel_value,
4139                },
4140            ))),
4141            CssProperty::MinHeight(_) => Some(CssProperty::MinHeight(CssPropertyValue::Exact(
4142                LayoutMinHeight {
4143                    inner: resolved_pixel_value,
4144                },
4145            ))),
4146            CssProperty::MaxWidth(_) => Some(CssProperty::MaxWidth(CssPropertyValue::Exact(
4147                LayoutMaxWidth {
4148                    inner: resolved_pixel_value,
4149                },
4150            ))),
4151            CssProperty::MaxHeight(_) => Some(CssProperty::MaxHeight(CssPropertyValue::Exact(
4152                LayoutMaxHeight {
4153                    inner: resolved_pixel_value,
4154                },
4155            ))),
4156            CssProperty::SelectionRadius(_) => Some(CssProperty::SelectionRadius(
4157                CssPropertyValue::Exact(SelectionRadius {
4158                    inner: resolved_pixel_value,
4159                }),
4160            )),
4161            _ => None,
4162        }
4163    }
4164
4165    /// Build a dependency chain for a CSS property value.
4166    ///
4167    /// This analyzes the property value and creates a chain of dependencies that can be
4168    /// resolved later during layout. For example:
4169    ///
4170    /// - `font-size: 16px` → Absolute chain with 16.0 pixels
4171    /// - `font-size: 1.5em` → Em chain depending on parent's font-size
4172    /// - `font-size: 150%` → Percent chain depending on parent's font-size
4173    /// - `padding: 2em` → Em chain depending on current node's font-size
4174    ///
4175    /// # Arguments
4176    ///
4177    /// * `node_id` - The node this property belongs to
4178    /// * `parent_id` - The parent node (for inheritance)
4179    /// * `property` - The CSS property to analyze
4180    ///
4181    /// Returns a dependency chain, or None if the property doesn't support chaining
4182    fn build_dependency_chain(
4183        &self,
4184        node_id: NodeId,
4185        parent_id: Option<NodeId>,
4186        property: &CssProperty,
4187    ) -> Option<CssDependencyChain> {
4188        use azul_css::props::basic::{length::SizeMetric, pixel::PixelValue};
4189
4190        let prop_type = property.get_type();
4191
4192        // For now, only handle font-size dependency chains
4193        // Other properties will be handled during layout resolution
4194        if prop_type != CssPropertyType::FontSize {
4195            return None;
4196        }
4197
4198        // Extract PixelValue from FontSize property
4199        let pixel_value = match property {
4200            CssProperty::FontSize(val) => val.get_property().map(|v| &v.inner)?,
4201            _ => return None,
4202        };
4203
4204        let number = pixel_value.number.get();
4205
4206        // For font-size: em/% refers to parent's font-size
4207        let source_node = parent_id?;
4208
4209        match pixel_value.metric {
4210            SizeMetric::Px => Some(CssDependencyChain::absolute(prop_type, number)),
4211            SizeMetric::Pt => {
4212                // 1pt = 1.333333px
4213                Some(CssDependencyChain::absolute(prop_type, number * 1.333333))
4214            }
4215            SizeMetric::Em => Some(CssDependencyChain::em(prop_type, source_node, number)),
4216            SizeMetric::Rem => {
4217                // Rem refers to root font-size
4218                Some(CssDependencyChain::rem(prop_type, number))
4219            }
4220            SizeMetric::Percent => Some(CssDependencyChain::percent(
4221                prop_type,
4222                source_node,
4223                number / 100.0,
4224            )),
4225            SizeMetric::In => {
4226                // 1in = 96px
4227                Some(CssDependencyChain::absolute(prop_type, number * 96.0))
4228            }
4229            SizeMetric::Cm => {
4230                // 1cm = 37.7952755906px
4231                Some(CssDependencyChain::absolute(
4232                    prop_type,
4233                    number * 37.7952755906,
4234                ))
4235            }
4236            SizeMetric::Mm => {
4237                // 1mm = 3.7795275591px
4238                Some(CssDependencyChain::absolute(
4239                    prop_type,
4240                    number * 3.7795275591,
4241                ))
4242            }
4243            // Viewport units: Cannot be resolved via dependency chain, need viewport context
4244            // These should be resolved at layout time using ResolutionContext
4245            SizeMetric::Vw | SizeMetric::Vh | SizeMetric::Vmin | SizeMetric::Vmax => {
4246                // For now, treat as unresolvable (need viewport size at layout time)
4247                None
4248            }
4249        }
4250    }
4251
4252    /// Applies user-agent (UA) CSS properties to the cascade before inheritance.
4253    ///
4254    /// UA CSS has the lowest priority in the cascade, so it should only be applied
4255    /// if the node doesn't already have the property from inline styles or author CSS.
4256    ///
4257    /// This is critical for text nodes: UA CSS properties (like font-weight: bold for H1)
4258    /// must be in the cascade maps so they can be inherited by child text nodes.
4259    pub fn apply_ua_css(&mut self, node_data: &[NodeData]) {
4260        use azul_css::props::property::CssPropertyType;
4261
4262        // Apply UA CSS to all nodes
4263        for (node_index, node) in node_data.iter().enumerate() {
4264            let node_id = NodeId::new(node_index);
4265            let node_type = &node.node_type;
4266
4267            // Get all possible CSS property types and check if UA CSS defines them
4268            // We need to check all properties that this node type might have
4269            let property_types = [
4270                CssPropertyType::Display,
4271                CssPropertyType::Width,
4272                CssPropertyType::Height,
4273                CssPropertyType::FontSize,
4274                CssPropertyType::FontWeight,
4275                CssPropertyType::FontFamily,
4276                CssPropertyType::MarginTop,
4277                CssPropertyType::MarginBottom,
4278                CssPropertyType::MarginLeft,
4279                CssPropertyType::MarginRight,
4280                CssPropertyType::PaddingTop,
4281                CssPropertyType::PaddingBottom,
4282                CssPropertyType::PaddingLeft,
4283                CssPropertyType::PaddingRight,
4284                // Add more as needed
4285            ];
4286
4287            for prop_type in &property_types {
4288                // Check if UA CSS defines this property for this node type
4289                if let Some(ua_prop) = crate::ua_css::get_ua_property(node_type, *prop_type) {
4290                    // Only insert if the property is NOT already set by inline CSS or author CSS
4291                    // UA CSS has LOWEST priority
4292                    let has_inline = node.css_props.iter().any(|p| {
4293                        // Check if this is a normal (unconditional) property
4294                        let is_normal = p.apply_if.as_slice().is_empty();
4295                        is_normal && p.property.get_type() == *prop_type
4296                    });
4297
4298                    let has_css = self
4299                        .css_normal_props
4300                        .get(&node_id)
4301                        .map(|map| map.contains_key(prop_type))
4302                        .unwrap_or(false);
4303
4304                    let has_cascaded = self
4305                        .cascaded_normal_props
4306                        .get(&node_id)
4307                        .map(|map| map.contains_key(prop_type))
4308                        .unwrap_or(false);
4309
4310                    // Insert UA CSS only if not already present (lowest priority)
4311                    if !has_inline && !has_css && !has_cascaded {
4312                        self.cascaded_normal_props
4313                            .entry(node_id)
4314                            .or_insert_with(|| BTreeMap::new())
4315                            .entry(*prop_type)
4316                            .or_insert_with(|| ua_prop.clone());
4317                    }
4318                }
4319            }
4320        }
4321    }
4322
4323    /// Compute inherited values and dependency chains for all nodes in the DOM tree.
4324    ///
4325    /// Implements CSS inheritance: walk tree depth-first, apply cascade priority
4326    /// (inherited → cascaded → css → inline → user), create dependency chains for
4327    /// relative values. Call `apply_ua_css()` before this function.
4328    pub fn compute_inherited_values(
4329        &mut self,
4330        node_hierarchy: &[NodeHierarchyItem],
4331        node_data: &[NodeData],
4332    ) -> Vec<NodeId> {
4333        node_hierarchy
4334            .iter()
4335            .enumerate()
4336            .filter_map(|(node_index, hierarchy_item)| {
4337                let node_id = NodeId::new(node_index);
4338                let parent_id = hierarchy_item.parent_id();
4339                let parent_computed =
4340                    parent_id.and_then(|pid| self.computed_values.get(&pid).cloned());
4341
4342                let mut ctx = InheritanceContext {
4343                    node_id,
4344                    parent_id,
4345                    computed_values: BTreeMap::new(),
4346                    dependency_chains: BTreeMap::new(),
4347                };
4348
4349                // Step 1: Inherit from parent
4350                if let Some(ref parent_values) = parent_computed {
4351                    self.inherit_from_parent(&mut ctx, parent_values);
4352                }
4353
4354                // Steps 2-5: Apply cascade in priority order
4355                self.apply_cascade_properties(
4356                    &mut ctx,
4357                    node_id,
4358                    &parent_computed,
4359                    node_data,
4360                    node_index,
4361                );
4362
4363                // Check for changes and store
4364                let changed = self.store_if_changed(&ctx);
4365                changed.then_some(node_id)
4366            })
4367            .collect()
4368    }
4369
4370    /// Inherit inheritable properties from parent node
4371    fn inherit_from_parent(
4372        &self,
4373        ctx: &mut InheritanceContext,
4374        parent_values: &BTreeMap<CssPropertyType, CssPropertyWithOrigin>,
4375    ) {
4376        for (prop_type, prop_with_origin) in
4377            parent_values.iter().filter(|(pt, _)| pt.is_inheritable())
4378        {
4379            ctx.computed_values.insert(
4380                *prop_type,
4381                CssPropertyWithOrigin {
4382                    property: prop_with_origin.property.clone(),
4383                    origin: CssPropertyOrigin::Inherited,
4384                },
4385            );
4386
4387            // Don't inherit font-size chains (would cause double resolution)
4388            if *prop_type == CssPropertyType::FontSize {
4389                continue;
4390            }
4391
4392            if let Some(chain) = ctx
4393                .parent_id
4394                .and_then(|pid| self.dependency_chains.get(&pid))
4395                .and_then(|chains| chains.get(prop_type))
4396            {
4397                ctx.dependency_chains.insert(*prop_type, chain.clone());
4398            }
4399        }
4400    }
4401
4402    /// Apply all cascade properties in priority order
4403    fn apply_cascade_properties(
4404        &self,
4405        ctx: &mut InheritanceContext,
4406        node_id: NodeId,
4407        parent_computed: &Option<BTreeMap<CssPropertyType, CssPropertyWithOrigin>>,
4408        node_data: &[NodeData],
4409        node_index: usize,
4410    ) {
4411        // Step 2: Cascaded properties (UA CSS)
4412        if let Some(cascaded_props) = self.cascaded_normal_props.get(&node_id).cloned() {
4413            for (prop_type, prop) in cascaded_props.iter() {
4414                if self.should_apply_cascaded(&ctx.computed_values, *prop_type, prop) {
4415                    self.process_property(ctx, prop, parent_computed);
4416                }
4417            }
4418        }
4419
4420        // Step 3: CSS properties (stylesheets)
4421        if let Some(css_props) = self.css_normal_props.get(&node_id) {
4422            for (_, prop) in css_props.iter() {
4423                self.process_property(ctx, prop, parent_computed);
4424            }
4425        }
4426
4427        // Step 4: Inline CSS properties
4428        for inline_prop in node_data[node_index].css_props.iter() {
4429            // Only apply unconditional (normal) properties
4430            if inline_prop.apply_if.as_slice().is_empty() {
4431                self.process_property(ctx, &inline_prop.property, parent_computed);
4432            }
4433        }
4434
4435        // Step 5: User-overridden properties
4436        if let Some(user_props) = self.user_overridden_properties.get(&node_id) {
4437            for (_, prop) in user_props.iter() {
4438                self.process_property(ctx, prop, parent_computed);
4439            }
4440        }
4441    }
4442
4443    /// Check if a cascaded property should be applied
4444    fn should_apply_cascaded(
4445        &self,
4446        computed: &BTreeMap<CssPropertyType, CssPropertyWithOrigin>,
4447        prop_type: CssPropertyType,
4448        prop: &CssProperty,
4449    ) -> bool {
4450        // Skip relative font-size if we already have inherited resolved value
4451        if prop_type == CssPropertyType::FontSize {
4452            if let Some(existing) = computed.get(&prop_type) {
4453                if existing.origin == CssPropertyOrigin::Inherited
4454                    && Self::has_relative_font_size_unit(prop)
4455                {
4456                    return false;
4457                }
4458            }
4459        }
4460
4461        match computed.get(&prop_type) {
4462            None => true,
4463            Some(existing) => existing.origin == CssPropertyOrigin::Inherited,
4464        }
4465    }
4466
4467    /// Process a single property: resolve dependencies and store
4468    fn process_property(
4469        &self,
4470        ctx: &mut InheritanceContext,
4471        prop: &CssProperty,
4472        parent_computed: &Option<BTreeMap<CssPropertyType, CssPropertyWithOrigin>>,
4473    ) {
4474        let prop_type = prop.get_type();
4475
4476        let resolved = if prop_type == CssPropertyType::FontSize {
4477            self.resolve_font_size_property(prop, parent_computed)
4478        } else {
4479            self.resolve_other_property(prop, &ctx.computed_values)
4480        };
4481
4482        ctx.computed_values.insert(
4483            prop_type,
4484            CssPropertyWithOrigin {
4485                property: resolved.clone(),
4486                origin: CssPropertyOrigin::Own,
4487            },
4488        );
4489
4490        if let Some(chain) = self.build_dependency_chain(ctx.node_id, ctx.parent_id, &resolved) {
4491            ctx.dependency_chains.insert(prop_type, chain);
4492        }
4493    }
4494
4495    /// Resolve font-size property (uses parent's font-size as reference)
4496    fn resolve_font_size_property(
4497        &self,
4498        prop: &CssProperty,
4499        parent_computed: &Option<BTreeMap<CssPropertyType, CssPropertyWithOrigin>>,
4500    ) -> CssProperty {
4501        const DEFAULT_FONT_SIZE_PX: f32 = 16.0;
4502
4503        let parent_font_size = parent_computed
4504            .as_ref()
4505            .and_then(|p| p.get(&CssPropertyType::FontSize));
4506
4507        match parent_font_size {
4508            Some(pfs) => Self::resolve_property_dependency(prop, &pfs.property)
4509                .unwrap_or_else(|| Self::resolve_font_size_to_pixels(prop, DEFAULT_FONT_SIZE_PX)),
4510            None => Self::resolve_font_size_to_pixels(prop, DEFAULT_FONT_SIZE_PX),
4511        }
4512    }
4513
4514    /// Resolve other properties (uses current node's font-size as reference)
4515    fn resolve_other_property(
4516        &self,
4517        prop: &CssProperty,
4518        computed: &BTreeMap<CssPropertyType, CssPropertyWithOrigin>,
4519    ) -> CssProperty {
4520        computed
4521            .get(&CssPropertyType::FontSize)
4522            .and_then(|fs| Self::resolve_property_dependency(prop, &fs.property))
4523            .unwrap_or_else(|| prop.clone())
4524    }
4525
4526    /// Convert font-size to absolute pixels
4527    fn resolve_font_size_to_pixels(prop: &CssProperty, reference_px: f32) -> CssProperty {
4528        use azul_css::{
4529            css::CssPropertyValue,
4530            props::basic::{font::StyleFontSize, length::SizeMetric, pixel::PixelValue},
4531        };
4532
4533        const DEFAULT_FONT_SIZE_PX: f32 = 16.0;
4534
4535        let CssProperty::FontSize(css_val) = prop else {
4536            return prop.clone();
4537        };
4538
4539        let Some(font_size) = css_val.get_property() else {
4540            return prop.clone();
4541        };
4542
4543        let resolved_px = match font_size.inner.metric {
4544            SizeMetric::Px => font_size.inner.number.get(),
4545            SizeMetric::Pt => font_size.inner.number.get() * 1.333333,
4546            SizeMetric::In => font_size.inner.number.get() * 96.0,
4547            SizeMetric::Cm => font_size.inner.number.get() * 37.7952755906,
4548            SizeMetric::Mm => font_size.inner.number.get() * 3.7795275591,
4549            SizeMetric::Em => font_size.inner.number.get() * reference_px,
4550            SizeMetric::Rem => font_size.inner.number.get() * DEFAULT_FONT_SIZE_PX,
4551            SizeMetric::Percent => font_size.inner.number.get() / 100.0 * reference_px,
4552            SizeMetric::Vw | SizeMetric::Vh | SizeMetric::Vmin | SizeMetric::Vmax => {
4553                return prop.clone();
4554            }
4555        };
4556
4557        CssProperty::FontSize(CssPropertyValue::Exact(StyleFontSize {
4558            inner: PixelValue::px(resolved_px),
4559        }))
4560    }
4561
4562    /// Check if font-size has relative unit (em, rem, %)
4563    fn has_relative_font_size_unit(prop: &CssProperty) -> bool {
4564        use azul_css::props::basic::length::SizeMetric;
4565
4566        let CssProperty::FontSize(css_val) = prop else {
4567            return false;
4568        };
4569
4570        css_val
4571            .get_property()
4572            .map(|fs| {
4573                matches!(
4574                    fs.inner.metric,
4575                    SizeMetric::Em | SizeMetric::Rem | SizeMetric::Percent
4576                )
4577            })
4578            .unwrap_or(false)
4579    }
4580
4581    /// Store computed values if changed, returns true if values were updated
4582    fn store_if_changed(&mut self, ctx: &InheritanceContext) -> bool {
4583        let values_changed = self
4584            .computed_values
4585            .get(&ctx.node_id)
4586            .map(|old| old != &ctx.computed_values)
4587            .unwrap_or(true);
4588
4589        let chains_changed = self
4590            .dependency_chains
4591            .get(&ctx.node_id)
4592            .map(|old| old != &ctx.dependency_chains)
4593            .unwrap_or(!ctx.dependency_chains.is_empty());
4594
4595        let changed = values_changed || chains_changed;
4596
4597        self.computed_values
4598            .insert(ctx.node_id, ctx.computed_values.clone());
4599        if !ctx.dependency_chains.is_empty() {
4600            self.dependency_chains
4601                .insert(ctx.node_id, ctx.dependency_chains.clone());
4602        }
4603
4604        changed
4605    }
4606}
4607
4608/// Context for computing inherited values for a single node
4609struct InheritanceContext {
4610    node_id: NodeId,
4611    parent_id: Option<NodeId>,
4612    computed_values: BTreeMap<CssPropertyType, CssPropertyWithOrigin>,
4613    dependency_chains: BTreeMap<CssPropertyType, CssDependencyChain>,
4614}
4615
4616impl CssPropertyCache {
4617    /// Resolve a dependency chain to an absolute pixel value.
4618    ///
4619    /// This walks through the chain and resolves each dependency:
4620    /// - Absolute values: return immediately
4621    /// - Em values: multiply by source node's font-size
4622    /// - Percent values: multiply by source node's property value
4623    /// - Rem values: multiply by root node's font-size
4624    ///
4625    /// # Arguments
4626    /// * `node_id` - The node to resolve the property for
4627    /// * `property_type` - The property type to resolve
4628    /// * `root_font_size` - Root element's font-size for rem calculations (default 16px)
4629    ///
4630    /// # Returns
4631    /// The resolved pixel value, or None if the chain couldn't be resolved
4632    pub fn resolve_dependency_chain(
4633        &self,
4634        node_id: NodeId,
4635        property_type: CssPropertyType,
4636        root_font_size: f32,
4637    ) -> Option<f32> {
4638        let chain = self.get_chain(node_id, property_type)?;
4639
4640        if let Some(cached) = chain.cached_pixels {
4641            return Some(cached);
4642        }
4643
4644        chain.steps.clone().iter().try_fold(None, |_, step| {
4645            Some(self.resolve_step(step, property_type, root_font_size))
4646        })?
4647    }
4648
4649    /// Get a dependency chain for a node/property pair
4650    fn get_chain(
4651        &self,
4652        node_id: NodeId,
4653        property_type: CssPropertyType,
4654    ) -> Option<&CssDependencyChain> {
4655        self.dependency_chains
4656            .get(&node_id)
4657            .and_then(|chains| chains.get(&property_type))
4658    }
4659
4660    /// Resolve a single step in a dependency chain
4661    fn resolve_step(
4662        &self,
4663        step: &CssDependencyChainStep,
4664        property_type: CssPropertyType,
4665        root_font_size: f32,
4666    ) -> Option<f32> {
4667        match step {
4668            CssDependencyChainStep::Absolute { pixels } => Some(*pixels),
4669            CssDependencyChainStep::Percent {
4670                source_node,
4671                factor,
4672            } => {
4673                let source_val = self.get_cached_pixels(*source_node, property_type)?;
4674                Some(source_val * factor)
4675            }
4676            CssDependencyChainStep::Em {
4677                source_node,
4678                factor,
4679            } => {
4680                let font_size = self.get_cached_pixels(*source_node, CssPropertyType::FontSize)?;
4681                Some(font_size * factor)
4682            }
4683            CssDependencyChainStep::Rem { factor } => Some(root_font_size * factor),
4684        }
4685    }
4686
4687    /// Get cached pixel value for a node's property
4688    fn get_cached_pixels(&self, node_id: NodeId, property_type: CssPropertyType) -> Option<f32> {
4689        self.get_chain(node_id, property_type)
4690            .and_then(|chain| chain.cached_pixels)
4691    }
4692
4693    /// Update a property value and invalidate all dependent chains.
4694    ///
4695    /// When a property changes (e.g., font-size changes from 16px to 20px):
4696    /// 1. Update the property value in computed_values
4697    /// 2. Update/rebuild the dependency chain
4698    /// 3. Find all nodes whose chains depend on this node
4699    /// 4. Invalidate their cached values
4700    /// 5. Return list of affected nodes that need re-layout
4701    ///
4702    /// # Arguments
4703    /// * `node_id` - The node whose property changed
4704    /// * `property` - The new property value
4705    /// * `node_hierarchy` - DOM tree (needed to find children)
4706    /// * `node_data` - Node data array
4707    ///
4708    /// # Returns
4709    /// Vector of NodeIds that were affected and need re-layout
4710    pub fn update_property_and_invalidate_dependents(
4711        &mut self,
4712        node_id: NodeId,
4713        property: CssProperty,
4714        node_hierarchy: &[NodeHierarchyItem],
4715        _node_data: &[NodeData],
4716    ) -> Vec<NodeId> {
4717        let prop_type = property.get_type();
4718
4719        self.update_computed_property(node_id, property.clone());
4720        self.rebuild_dependency_chain(node_id, prop_type, &property, node_hierarchy);
4721
4722        let mut affected = self.invalidate_dependents(node_id);
4723        affected.push(node_id);
4724        affected
4725    }
4726
4727    /// Update a property in computed_values with Own origin
4728    fn update_computed_property(&mut self, node_id: NodeId, property: CssProperty) {
4729        self.computed_values
4730            .entry(node_id)
4731            .or_insert_with(BTreeMap::new)
4732            .insert(
4733                property.get_type(),
4734                CssPropertyWithOrigin {
4735                    property,
4736                    origin: CssPropertyOrigin::Own,
4737                },
4738            );
4739    }
4740
4741    /// Rebuild the dependency chain for a property
4742    fn rebuild_dependency_chain(
4743        &mut self,
4744        node_id: NodeId,
4745        prop_type: CssPropertyType,
4746        property: &CssProperty,
4747        node_hierarchy: &[NodeHierarchyItem],
4748    ) {
4749        let parent_id = node_hierarchy
4750            .get(node_id.index())
4751            .and_then(|h| h.parent_id());
4752
4753        if let Some(chain) = self.build_dependency_chain(node_id, parent_id, property) {
4754            self.dependency_chains
4755                .entry(node_id)
4756                .or_insert_with(BTreeMap::new)
4757                .insert(prop_type, chain);
4758        }
4759    }
4760
4761    /// Invalidate cached values for all chains that depend on a node
4762    fn invalidate_dependents(&mut self, node_id: NodeId) -> Vec<NodeId> {
4763        self.dependency_chains
4764            .iter_mut()
4765            .filter_map(|(dep_node_id, chains)| {
4766                let affected = chains.values_mut().any(|chain| {
4767                    if chain.depends_on(node_id) {
4768                        chain.cached_pixels = None;
4769                        true
4770                    } else {
4771                        false
4772                    }
4773                });
4774                affected.then_some(*dep_node_id)
4775            })
4776            .collect()
4777    }
4778}
4779
4780#[cfg(test)]
4781mod tests {
4782    use super::*;
4783    use crate::dom::NodeType;
4784
4785    #[test]
4786    fn test_ua_css_p_tag_properties() {
4787        // Create an empty CssPropertyCache
4788        let cache = CssPropertyCache::empty(1);
4789
4790        // Create a minimal <p> tag NodeData using public API
4791        let mut node_data = NodeData::create_node(NodeType::P);
4792
4793        let node_id = NodeId::new(0);
4794        let node_state = StyledNodeState::default();
4795
4796        // Test that <p> has display: block from UA CSS
4797        let display = cache.get_display(&node_data, &node_id, &node_state);
4798        assert!(
4799            display.is_some(),
4800            "Expected <p> to have display property from UA CSS"
4801        );
4802        if let Some(d) = display {
4803            if let Some(display_value) = d.get_property() {
4804                assert_eq!(
4805                    *display_value,
4806                    LayoutDisplay::Block,
4807                    "Expected <p> to have display: block, got {:?}",
4808                    display_value
4809                );
4810            }
4811        }
4812
4813        // NOTE: <p> does NOT have width: 100% in standard UA CSS
4814        // Block elements have width: auto by default, which means "fill available width"
4815        // but it's not the same as width: 100%. The difference is critical for flexbox.
4816        let width = cache.get_width(&node_data, &node_id, &node_state);
4817        // Width should be None because <p> should use auto width (no explicit width property)
4818        assert!(
4819            width.is_none(),
4820            "Expected <p> to NOT have explicit width (should be auto), but got {:?}",
4821            width
4822        );
4823
4824        // Test that <p> does NOT have a default height from UA CSS
4825        // (height should be auto, which means None)
4826        let height = cache.get_height(&node_data, &node_id, &node_state);
4827        println!("Height for <p> tag: {:?}", height);
4828
4829        // Height should be None because <p> should use auto height
4830        assert!(
4831            height.is_none(),
4832            "Expected <p> to NOT have explicit height (should be auto), but got {:?}",
4833            height
4834        );
4835    }
4836
4837    #[test]
4838    fn test_ua_css_body_tag_properties() {
4839        let cache = CssPropertyCache::empty(1);
4840
4841        let node_data = NodeData::create_node(NodeType::Body);
4842
4843        let node_id = NodeId::new(0);
4844        let node_state = StyledNodeState::default();
4845
4846        // NOTE: <body> does NOT have width: 100% in standard UA CSS
4847        // It inherits from the Initial Containing Block (ICB) and has width: auto
4848        let width = cache.get_width(&node_data, &node_id, &node_state);
4849        // Width should be None because <body> should use auto width (no explicit width property)
4850        assert!(
4851            width.is_none(),
4852            "Expected <body> to NOT have explicit width (should be auto), but got {:?}",
4853            width
4854        );
4855
4856        // Note: height: 100% was removed from UA CSS (ua_css.rs:506 commented out)
4857        // This is correct - <body> should have height: auto by default per CSS spec
4858        let height = cache.get_height(&node_data, &node_id, &node_state);
4859        assert!(
4860            height.is_none(),
4861            "<body> should not have explicit height from UA CSS (should be auto)"
4862        );
4863
4864        // Test margins (body has 8px margins from UA CSS)
4865        let margin_top = cache.get_margin_top(&node_data, &node_id, &node_state);
4866        assert!(
4867            margin_top.is_some(),
4868            "Expected <body> to have margin-top from UA CSS"
4869        );
4870    }
4871}