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

            
24
extern crate alloc;
25

            
26
use alloc::{boxed::Box, string::String, vec::Vec};
27
use core::mem::ManuallyDrop;
28

            
29
use crate::dom::NodeType;
30

            
31
/// Tracks the origin of a CSS property value.
32
/// Used to correctly implement the CSS cascade and inheritance rules.
33
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
34
pub enum CssPropertyOrigin {
35
    /// Property was inherited from parent node (only for inheritable properties)
36
    Inherited,
37
    /// Property is the node's own value (from UA CSS, CSS file, inline style, or user override)
38
    Own,
39
}
40

            
41
/// A CSS property with its origin tracking.
42
#[derive(Debug, Clone, PartialEq)]
43
pub struct CssPropertyWithOrigin {
44
    pub property: CssProperty,
45
    pub origin: CssPropertyOrigin,
46
}
47

            
48
use azul_css::{
49
    css::{Css, CssPath},
50
    props::{
51
        basic::{StyleFontFamily, StyleFontFamilyVec, StyleFontSize},
52
        layout::{LayoutDisplay, LayoutHeight, LayoutWidth},
53
        property::{
54
            BoxDecorationBreakValue, BreakInsideValue, CaretAnimationDurationValue,
55
            CaretColorValue, CaretWidthValue, ClipPathValue, ColumnCountValue, ColumnFillValue,
56
            ColumnRuleColorValue, ColumnRuleStyleValue, ColumnRuleWidthValue, ColumnSpanValue,
57
            ColumnWidthValue, ContentValue, CounterIncrementValue, CounterResetValue, CssProperty,
58
            CssPropertyType, FlowFromValue, FlowIntoValue, LayoutAlignContentValue,
59
            LayoutAlignItemsValue, LayoutAlignSelfValue, LayoutBorderBottomWidthValue,
60
            LayoutBorderLeftWidthValue, LayoutBorderRightWidthValue, LayoutBorderSpacingValue,
61
            LayoutBorderTopWidthValue, LayoutBoxSizingValue, LayoutClearValue,
62
            LayoutColumnGapValue, LayoutDisplayValue, LayoutFlexBasisValue,
63
            LayoutFlexDirectionValue, LayoutFlexGrowValue, LayoutFlexShrinkValue,
64
            LayoutFlexWrapValue, LayoutFloatValue, LayoutGapValue, LayoutGridAutoColumnsValue,
65
            LayoutGridAutoFlowValue, LayoutGridAutoRowsValue, LayoutGridColumnValue,
66
            LayoutGridRowValue, LayoutGridTemplateColumnsValue, LayoutGridTemplateRowsValue,
67
            LayoutHeightValue, LayoutInsetBottomValue, LayoutJustifyContentValue,
68
            LayoutJustifyItemsValue, LayoutJustifySelfValue, LayoutLeftValue,
69
            LayoutMarginBottomValue, LayoutMarginLeftValue, LayoutMarginRightValue,
70
            LayoutMarginTopValue, LayoutMaxHeightValue, LayoutMaxWidthValue, LayoutMinHeightValue,
71
            LayoutMinWidthValue, LayoutOverflowValue, LayoutPaddingBottomValue,
72
            LayoutPaddingLeftValue, LayoutPaddingRightValue, LayoutPaddingTopValue,
73
            LayoutPositionValue, LayoutRightValue, LayoutRowGapValue, LayoutScrollbarWidthValue,
74
            LayoutTableLayoutValue, LayoutTextJustifyValue, LayoutTopValue, LayoutWidthValue,
75
            LayoutWritingModeValue, LayoutZIndexValue, OrphansValue, PageBreakValue,
76
            StyleBackgroundContentValue, ScrollbarFadeDelayValue, ScrollbarFadeDurationValue,
77
            ScrollbarVisibilityModeValue, SelectionBackgroundColorValue, SelectionColorValue,
78
            SelectionRadiusValue, ShapeImageThresholdValue, ShapeInsideValue, ShapeMarginValue,
79
            ShapeOutsideValue, StringSetValue, StyleBackfaceVisibilityValue,
80
            StyleBackgroundContentVecValue, StyleBackgroundPositionVecValue,
81
            StyleBackgroundRepeatVecValue, StyleBackgroundSizeVecValue,
82
            StyleBorderBottomColorValue, StyleBorderBottomLeftRadiusValue,
83
            StyleBorderBottomRightRadiusValue, StyleBorderBottomStyleValue,
84
            StyleBorderCollapseValue, StyleBorderLeftColorValue, StyleBorderLeftStyleValue,
85
            StyleBorderRightColorValue, StyleBorderRightStyleValue, StyleBorderTopColorValue,
86
            StyleBorderTopLeftRadiusValue, StyleBorderTopRightRadiusValue,
87
            StyleBorderTopStyleValue, StyleBoxShadowValue, StyleCaptionSideValue, StyleCursorValue,
88
            StyleDirectionValue, StyleEmptyCellsValue, StyleExclusionMarginValue,
89
            StyleFilterVecValue, StyleFontFamilyVecValue, StyleFontSizeValue, StyleFontStyleValue,
90
            StyleFontValue, StyleFontWeightValue, StyleHangingPunctuationValue,
91
            StyleHyphenationLanguageValue, StyleHyphensValue, StyleInitialLetterValue,
92
            StyleLetterSpacingValue, StyleLineBreakValue, StyleLineClampValue, StyleLineHeightValue,
93
            StyleListStylePositionValue, StyleListStyleTypeValue, StyleMixBlendModeValue,
94
            StyleAspectRatioValue, StyleObjectFitValue, StyleObjectPositionValue,
95
            StyleOpacityValue, StylePerspectiveOriginValue,
96
            StyleScrollbarColorValue, StyleOverflowWrapValue, StyleTabSizeValue,
97
            StyleTextAlignLastValue, StyleTextOrientationValue,
98
            StyleTextAlignValue, StyleTextColorValue,
99
            StyleTextCombineUprightValue, StyleUnicodeBidiValue,
100
            StyleTextBoxTrimValue, StyleTextBoxEdgeValue,
101
            StyleDominantBaselineValue, StyleAlignmentBaselineValue,
102
            StyleInitialLetterAlignValue, StyleInitialLetterWrapValue,
103
            StyleScrollbarGutterValue, StyleOverflowClipMarginValue, StyleClipRectValue,
104
            StyleTextDecorationValue, StyleTextIndentValue,
105
            StyleTransformOriginValue, StyleTransformVecValue, StyleUserSelectValue,
106
            StyleVerticalAlignValue, StyleVisibilityValue, StyleWhiteSpaceValue,
107
            StyleWordBreakValue, StyleWordSpacingValue, WidowsValue,
108
        },
109
        style::{StyleCursor, StyleTextColor, StyleTransformOrigin},
110
    },
111
    AzString,
112
};
113

            
114
use crate::{
115
    dom::{NodeData, NodeId, TabIndex, TagId},
116
    id::{NodeDataContainer, NodeDataContainerRef},
117
    style::CascadeInfo,
118
    styled_dom::{
119
        NodeHierarchyItem, NodeHierarchyItemId, NodeHierarchyItemVec, ParentWithNodeDepth,
120
        ParentWithNodeDepthVec, StyledNodeState, TagIdToNodeIdMapping,
121
    },
122
};
123

            
124
use azul_css::dynamic_selector::{
125
    CssPropertyWithConditions, CssPropertyWithConditionsVec, DynamicSelectorContext,
126
};
127

            
128
#[cfg(feature = "std")]
129
std::thread_local! {
130
    static PROP_COUNTS: core::cell::RefCell<
131
        std::collections::HashMap<&'static str, usize>
132
    > = core::cell::RefCell::new(std::collections::HashMap::new());
133
}
134

            
135
/// Drain the per-thread CSS cascade-walk counter populated by
136
/// [`CssPropertyCache::get_property`] when `AZ_PROP_COUNT=1` is set
137
/// in the environment. Returns `(property_label, count)` pairs
138
/// sorted by count descending. Layout-side instrumentation calls
139
/// this after each `layout_document` to print which properties
140
/// drove the most cascade walks.
141
#[cfg(feature = "std")]
142
pub fn drain_css_prop_counts() -> Vec<(&'static str, usize)> {
143
    // try_with: no real TLS in the lifted-to-wasm web backend (see the
144
    // get_property recording site) — return empty rather than panic.
145
    PROP_COUNTS
146
        .try_with(|c| {
147
            let map = core::mem::take(&mut *c.borrow_mut());
148
            let mut v: Vec<_> = map.into_iter().collect();
149
            v.sort_by(|a, b| b.1.cmp(&a.1));
150
            v
151
        })
152
        .unwrap_or_default()
153
}
154

            
155
// Unit conversion constants (CSS absolute units → pixels)
156
const PT_TO_PX: f32 = 1.333333;
157
const IN_TO_PX: f32 = 96.0;
158
const CM_TO_PX: f32 = 37.7952755906;
159
const MM_TO_PX: f32 = 3.7795275591;
160

            
161
/// Match on any CssProperty variant and access the inner CssPropertyValue<T>.
162
#[allow(unused_macros)]
163
macro_rules! match_property_value {
164
    ($property:expr, $value:ident, $expr:expr) => {
165
        match $property {
166
            CssProperty::CaretColor($value) => $expr,
167
            CssProperty::CaretAnimationDuration($value) => $expr,
168
            CssProperty::SelectionBackgroundColor($value) => $expr,
169
            CssProperty::SelectionColor($value) => $expr,
170
            CssProperty::SelectionRadius($value) => $expr,
171
            CssProperty::TextColor($value) => $expr,
172
            CssProperty::FontSize($value) => $expr,
173
            CssProperty::FontFamily($value) => $expr,
174
            CssProperty::FontWeight($value) => $expr,
175
            CssProperty::FontStyle($value) => $expr,
176
            CssProperty::TextAlign($value) => $expr,
177
            CssProperty::TextJustify($value) => $expr,
178
            CssProperty::VerticalAlign($value) => $expr,
179
            CssProperty::LetterSpacing($value) => $expr,
180
            CssProperty::TextIndent($value) => $expr,
181
            CssProperty::InitialLetter($value) => $expr,
182
            CssProperty::LineClamp($value) => $expr,
183
            CssProperty::HangingPunctuation($value) => $expr,
184
            CssProperty::TextCombineUpright($value) => $expr,
185
            CssProperty::UnicodeBidi($value) => $expr,
186
            CssProperty::TextBoxTrim($value) => $expr,
187
            CssProperty::TextBoxEdge($value) => $expr,
188
            CssProperty::DominantBaseline($value) => $expr,
189
            CssProperty::AlignmentBaseline($value) => $expr,
190
            CssProperty::InitialLetterAlign($value) => $expr,
191
            CssProperty::InitialLetterWrap($value) => $expr,
192
            CssProperty::ScrollbarGutter($value) => $expr,
193
            CssProperty::OverflowClipMargin($value) => $expr,
194
            CssProperty::Clip($value) => $expr,
195
            CssProperty::ExclusionMargin($value) => $expr,
196
            CssProperty::HyphenationLanguage($value) => $expr,
197
            CssProperty::LineHeight($value) => $expr,
198
            CssProperty::WordSpacing($value) => $expr,
199
            CssProperty::TabSize($value) => $expr,
200
            CssProperty::WhiteSpace($value) => $expr,
201
            CssProperty::Hyphens($value) => $expr,
202
            CssProperty::Direction($value) => $expr,
203
            CssProperty::UserSelect($value) => $expr,
204
            CssProperty::TextDecoration($value) => $expr,
205
            CssProperty::Cursor($value) => $expr,
206
            CssProperty::Display($value) => $expr,
207
            CssProperty::Float($value) => $expr,
208
            CssProperty::BoxSizing($value) => $expr,
209
            CssProperty::Width($value) => $expr,
210
            CssProperty::Height($value) => $expr,
211
            CssProperty::MinWidth($value) => $expr,
212
            CssProperty::MinHeight($value) => $expr,
213
            CssProperty::MaxWidth($value) => $expr,
214
            CssProperty::MaxHeight($value) => $expr,
215
            CssProperty::Position($value) => $expr,
216
            CssProperty::Top($value) => $expr,
217
            CssProperty::Right($value) => $expr,
218
            CssProperty::Left($value) => $expr,
219
            CssProperty::Bottom($value) => $expr,
220
            CssProperty::ZIndex($value) => $expr,
221
            CssProperty::FlexWrap($value) => $expr,
222
            CssProperty::FlexDirection($value) => $expr,
223
            CssProperty::FlexGrow($value) => $expr,
224
            CssProperty::FlexShrink($value) => $expr,
225
            CssProperty::FlexBasis($value) => $expr,
226
            CssProperty::JustifyContent($value) => $expr,
227
            CssProperty::AlignItems($value) => $expr,
228
            CssProperty::AlignContent($value) => $expr,
229
            CssProperty::AlignSelf($value) => $expr,
230
            CssProperty::JustifyItems($value) => $expr,
231
            CssProperty::JustifySelf($value) => $expr,
232
            CssProperty::BackgroundContent($value) => $expr,
233
            CssProperty::BackgroundPosition($value) => $expr,
234
            CssProperty::BackgroundSize($value) => $expr,
235
            CssProperty::BackgroundRepeat($value) => $expr,
236
            CssProperty::OverflowX($value) => $expr,
237
            CssProperty::OverflowY($value) => $expr,
238
            CssProperty::OverflowBlock($value) => $expr,
239
            CssProperty::OverflowInline($value) => $expr,
240
            CssProperty::PaddingTop($value) => $expr,
241
            CssProperty::PaddingLeft($value) => $expr,
242
            CssProperty::PaddingRight($value) => $expr,
243
            CssProperty::PaddingBottom($value) => $expr,
244
            CssProperty::MarginTop($value) => $expr,
245
            CssProperty::MarginLeft($value) => $expr,
246
            CssProperty::MarginRight($value) => $expr,
247
            CssProperty::MarginBottom($value) => $expr,
248
            CssProperty::BorderTopLeftRadius($value) => $expr,
249
            CssProperty::BorderTopRightRadius($value) => $expr,
250
            CssProperty::BorderBottomLeftRadius($value) => $expr,
251
            CssProperty::BorderBottomRightRadius($value) => $expr,
252
            CssProperty::BorderTopColor($value) => $expr,
253
            CssProperty::BorderRightColor($value) => $expr,
254
            CssProperty::BorderLeftColor($value) => $expr,
255
            CssProperty::BorderBottomColor($value) => $expr,
256
            CssProperty::BorderTopStyle($value) => $expr,
257
            CssProperty::BorderRightStyle($value) => $expr,
258
            CssProperty::BorderLeftStyle($value) => $expr,
259
            CssProperty::BorderBottomStyle($value) => $expr,
260
            CssProperty::BorderTopWidth($value) => $expr,
261
            CssProperty::BorderRightWidth($value) => $expr,
262
            CssProperty::BorderLeftWidth($value) => $expr,
263
            CssProperty::BorderBottomWidth($value) => $expr,
264
            CssProperty::BoxShadow($value) => $expr,
265
            CssProperty::Opacity($value) => $expr,
266
            CssProperty::Transform($value) => $expr,
267
            CssProperty::TransformOrigin($value) => $expr,
268
            CssProperty::PerspectiveOrigin($value) => $expr,
269
            CssProperty::BackfaceVisibility($value) => $expr,
270
            CssProperty::MixBlendMode($value) => $expr,
271
            CssProperty::Filter($value) => $expr,
272
            CssProperty::Visibility($value) => $expr,
273
            CssProperty::WritingMode($value) => $expr,
274
            CssProperty::GridTemplateColumns($value) => $expr,
275
            CssProperty::GridTemplateRows($value) => $expr,
276
            CssProperty::GridAutoColumns($value) => $expr,
277
            CssProperty::GridAutoRows($value) => $expr,
278
            CssProperty::GridAutoFlow($value) => $expr,
279
            CssProperty::GridColumn($value) => $expr,
280
            CssProperty::GridRow($value) => $expr,
281
            CssProperty::GridTemplateAreas($value) => $expr,
282
            CssProperty::Gap($value) => $expr,
283
            CssProperty::ColumnGap($value) => $expr,
284
            CssProperty::RowGap($value) => $expr,
285
            CssProperty::Clear($value) => $expr,
286
            CssProperty::ScrollbarTrack($value) => $expr,
287
            CssProperty::ScrollbarThumb($value) => $expr,
288
            CssProperty::ScrollbarButton($value) => $expr,
289
            CssProperty::ScrollbarCorner($value) => $expr,
290
            CssProperty::ScrollbarResizer($value) => $expr,
291
            CssProperty::ScrollbarWidth($value) => $expr,
292
            CssProperty::ScrollbarColor($value) => $expr,
293
            CssProperty::ListStyleType($value) => $expr,
294
            CssProperty::ListStylePosition($value) => $expr,
295
            CssProperty::Font($value) => $expr,
296
            CssProperty::ColumnCount($value) => $expr,
297
            CssProperty::ColumnWidth($value) => $expr,
298
            CssProperty::ColumnSpan($value) => $expr,
299
            CssProperty::ColumnFill($value) => $expr,
300
            CssProperty::ColumnRuleStyle($value) => $expr,
301
            CssProperty::ColumnRuleWidth($value) => $expr,
302
            CssProperty::ColumnRuleColor($value) => $expr,
303
            CssProperty::FlowInto($value) => $expr,
304
            CssProperty::FlowFrom($value) => $expr,
305
            CssProperty::ShapeOutside($value) => $expr,
306
            CssProperty::ShapeInside($value) => $expr,
307
            CssProperty::ShapeImageThreshold($value) => $expr,
308
            CssProperty::ShapeMargin($value) => $expr,
309
            CssProperty::ClipPath($value) => $expr,
310
            CssProperty::Content($value) => $expr,
311
            CssProperty::CounterIncrement($value) => $expr,
312
            CssProperty::CounterReset($value) => $expr,
313
            CssProperty::StringSet($value) => $expr,
314
            CssProperty::Orphans($value) => $expr,
315
            CssProperty::Widows($value) => $expr,
316
            CssProperty::PageBreakBefore($value) => $expr,
317
            CssProperty::PageBreakAfter($value) => $expr,
318
            CssProperty::PageBreakInside($value) => $expr,
319
            CssProperty::BreakInside($value) => $expr,
320
            CssProperty::BoxDecorationBreak($value) => $expr,
321
            CssProperty::TableLayout($value) => $expr,
322
            CssProperty::BorderCollapse($value) => $expr,
323
            CssProperty::BorderSpacing($value) => $expr,
324
            CssProperty::CaptionSide($value) => $expr,
325
            CssProperty::EmptyCells($value) => $expr,
326
        }
327
    };
328
}
329

            
330
/// A CSS property tagged with its pseudo-state and property type.
331
/// Replaces the per-pseudo-state BTreeMap approach: instead of 6 BTreeMaps
332
/// per node (Normal/Hover/Active/Focus/Dragging/DragOver), we store one Vec
333
/// per node and tag each property with its state. Lookups use `.iter().find()`.
334
#[derive(Debug, Clone, PartialEq)]
335
pub struct StatefulCssProperty {
336
    pub state: azul_css::dynamic_selector::PseudoStateType,
337
    pub prop_type: CssPropertyType,
338
    pub property: CssProperty,
339
}
340

            
341
// =============================================================================
342
// FlatVecVec: Cache-friendly replacement for Vec<Vec<T>>
343
// =============================================================================
344

            
345
/// A flat, cache-friendly replacement for `Vec<Vec<T>>`.
346
///
347
/// During the **build phase**, items are pushed into per-node inner Vecs
348
/// (same as before). After building is complete, `flatten()` compacts all
349
/// inner Vecs into a single contiguous `Vec<T>` with a `(start, len)` offset
350
/// table per node. All subsequent reads use the flat layout, eliminating
351
/// N heap allocations and pointer chasing.
352
///
353
/// ## Lifecycle
354
///
355
/// ```text
356
/// new(n) → push_to(idx, item)* → sort_each_and_flatten(key_fn) → get_slice(idx)*
357
///          ── build phase ──       ── transition ──                ── read phase ──
358
/// ```
359
#[derive(Debug, Clone)]
360
pub struct FlatVecVec<T> {
361
    /// Per-node inner Vecs (used during build phase, empty after flatten).
362
    build: Vec<Vec<T>>,
363
    /// Flat contiguous storage (populated after flatten).
364
    data: Vec<T>,
365
    /// `(start, len)` offsets into `data` for each node (populated after flatten).
366
    offsets: Vec<(u32, u32)>,
367
}
368

            
369
impl<T: PartialEq> PartialEq for FlatVecVec<T> {
370
    fn eq(&self, other: &Self) -> bool {
371
        let self_in_build = !self.build.is_empty() && self.offsets.is_empty();
372
        let other_in_build = !other.build.is_empty() && other.offsets.is_empty();
373
        debug_assert!(
374
            self_in_build == other_in_build,
375
            "FlatVecVec::eq called across phases (one build, one flattened)"
376
        );
377
        if self_in_build || other_in_build {
378
            self.build == other.build
379
        } else {
380
            self.data == other.data && self.offsets == other.offsets
381
        }
382
    }
383
}
384

            
385
impl<T> Default for FlatVecVec<T> {
386
    fn default() -> Self {
387
        Self {
388
            build: Vec::new(),
389
            data: Vec::new(),
390
            offsets: Vec::new(),
391
        }
392
    }
393
}
394

            
395
impl<T> FlatVecVec<T> {
396
    /// Approximate heap bytes retained. Sums capacity of the
397
    /// flattened `data` + `offsets` tables and the per-node build
398
    /// Vecs (in case `sort_each_and_flatten` hasn't been called
399
    /// yet). `per_element_size` should be `size_of::<T>()`.
400
4
    pub fn heap_bytes(&self, per_element_size: usize) -> usize {
401
4
        let data_bytes = self.data.capacity() * per_element_size;
402
4
        let offsets_bytes =
403
4
            self.offsets.capacity() * core::mem::size_of::<(u32, u32)>();
404
4
        let mut build_bytes = self.build.capacity() * core::mem::size_of::<Vec<T>>();
405
104
        for v in &self.build {
406
100
            build_bytes += v.capacity() * per_element_size;
407
100
        }
408
4
        data_bytes + offsets_bytes + build_bytes
409
4
    }
410

            
411
    /// Create a new `FlatVecVec` with `node_count` empty slots (build phase).
412
24292
    pub fn new(node_count: usize) -> Self {
413
24292
        let mut build = Vec::with_capacity(node_count);
414
170846
        for _ in 0..node_count {
415
170846
            build.push(Vec::new());
416
170846
        }
417
24292
        Self {
418
24292
            build,
419
24292
            data: Vec::new(),
420
24292
            offsets: Vec::new(),
421
24292
        }
422
24292
    }
423

            
424
    /// Push an item to the inner Vec at `node_index` (build phase).
425
    ///
426
    /// # Panics
427
    /// Panics if already flattened or if `node_index >= len()`.
428
    #[inline]
429
458406
    pub fn push_to(&mut self, node_index: usize, item: T) {
430
458406
        self.build[node_index].push(item);
431
458406
    }
432

            
433
    /// Get a mutable reference to the inner Vec at `node_index` (build phase).
434
    #[inline]
435
13807
    pub fn build_mut(&mut self, node_index: usize) -> &mut Vec<T> {
436
13807
        &mut self.build[node_index]
437
13807
    }
438

            
439
    /// Iterate mutably over all inner Vecs (build phase, e.g. for clearing).
440
    #[inline]
441
8099
    pub fn build_iter_mut(&mut self) -> core::slice::IterMut<'_, Vec<T>> {
442
8099
        self.build.iter_mut()
443
8099
    }
444

            
445
    /// Get a reference to the inner Vec at `node_index` during build phase.
446
    /// During read phase, returns None (use `get_slice` instead).
447
    #[inline]
448
    pub fn build_get(&self, node_index: usize) -> Option<&Vec<T>> {
449
        self.build.get(node_index)
450
    }
451

            
452
    /// Number of node slots.
453
    #[inline]
454
24074
    pub fn len(&self) -> usize {
455
24074
        if !self.offsets.is_empty() {
456
12037
            self.offsets.len()
457
        } else {
458
12037
            self.build.len()
459
        }
460
24074
    }
461

            
462
    /// Returns true if this is in read (flattened) mode.
463
    #[inline]
464
12058
    pub fn is_flattened(&self) -> bool {
465
12058
        !self.offsets.is_empty() || self.build.is_empty()
466
12058
    }
467

            
468
    /// Get a slice for the node at `node_index` (read phase).
469
    /// Returns empty slice if index is out of bounds or not yet flattened
470
    /// (falls back to build-phase data if not yet flattened).
471
    #[inline]
472
5386813
    pub fn get_slice(&self, node_index: usize) -> &[T] {
473
5386813
        if !self.offsets.is_empty() {
474
            // Read phase: use flat data
475
4735989
            if let Some(&(start, len)) = self.offsets.get(node_index) {
476
4735989
                let s = start as usize;
477
4735989
                let l = len as usize;
478
4735989
                &self.data[s..s + l]
479
            } else {
480
                &[]
481
            }
482
        } else {
483
            // Build phase fallback: use inner Vecs
484
650824
            self.build.get(node_index).map(|v| v.as_slice()).unwrap_or(&[])
485
        }
486
5386813
    }
487

            
488
    /// Flatten: sort each inner Vec by key, deduplicate by keeping the last
489
    /// occurrence of each key (CSS cascade: later source order wins among
490
    /// equal specificity), then compact into flat storage.
491
    /// Drains all build-phase Vecs. After this call, only `get_slice()` works.
492
24095
    pub fn sort_each_and_flatten<K: Ord + Eq>(&mut self, key_fn: impl Fn(&T) -> K) {
493
24095
        let node_count = self.build.len();
494
170573
        let total: usize = self.build.iter().map(|v| v.len()).sum();
495

            
496
24095
        let mut flat_data = Vec::with_capacity(total);
497
24095
        let mut offsets = Vec::with_capacity(node_count);
498

            
499
170573
        for inner in self.build.iter_mut() {
500
1157861
            inner.sort_by(|a, b| key_fn(a).cmp(&key_fn(b)));
501

            
502
            // Deduplicate: keep last of each consecutive-key group (CSS cascade).
503
170573
            let n = inner.len();
504
170573
            let mut keep = vec![false; n];
505
493200
            for i in 0..n {
506
493200
                if i + 1 >= n || key_fn(&inner[i]) != key_fn(&inner[i + 1]) {
507
493100
                    keep[i] = true;
508
493100
                }
509
            }
510

            
511
170573
            let start = flat_data.len() as u32;
512
            // Drain inner and push only kept items
513
493200
            for (i, item) in inner.drain(..).enumerate() {
514
493200
                if keep[i] {
515
493100
                    flat_data.push(item);
516
493100
                }
517
            }
518

            
519
170573
            let len = (flat_data.len() as u32) - start;
520
170573
            offsets.push((start, len));
521
        }
522

            
523
24095
        flat_data.shrink_to_fit();
524
24095
        self.data = flat_data;
525
24095
        self.offsets = offsets;
526
24095
        self.build = Vec::new();
527
24095
    }
528

            
529
    /// Flatten without sorting (for data that's already sorted).
530
    pub fn flatten(&mut self) {
531
        let node_count = self.build.len();
532
        let total: usize = self.build.iter().map(|v| v.len()).sum();
533

            
534
        let mut flat_data = Vec::with_capacity(total);
535
        let mut offsets = Vec::with_capacity(node_count);
536

            
537
        for inner in self.build.iter_mut() {
538
            let start = flat_data.len() as u32;
539
            let len = inner.len() as u32;
540
            offsets.push((start, len));
541
            flat_data.append(inner);
542
        }
543

            
544
        self.data = flat_data;
545
        self.offsets = offsets;
546
        self.build = Vec::new();
547
    }
548

            
549
    /// Rebuild flat storage, keeping only items matching `predicate`.
550
    /// Must be called after flatten. Preserves per-node ordering.
551
12057
    pub fn retain(&mut self, predicate: impl Fn(&T) -> bool) where T: Clone {
552
12057
        if self.offsets.is_empty() { return; }
553
12057
        let node_count = self.offsets.len();
554
12057
        let mut new_data = Vec::new();
555
12057
        let mut new_offsets = Vec::with_capacity(node_count);
556
97346
        for &(start, len) in &self.offsets {
557
85289
            let s = start as usize;
558
85289
            let l = len as usize;
559
85289
            let new_start = new_data.len() as u32;
560
85289
            let slice = &self.data[s..s + l];
561
85289
            let mut kept = 0u32;
562
281460
            for item in slice {
563
196171
                if predicate(item) {
564
35500
                    new_data.push((*item).clone());
565
35500
                    kept += 1;
566
160671
                }
567
            }
568
85289
            new_offsets.push((new_start, kept));
569
        }
570
12057
        new_data.shrink_to_fit();
571
12057
        self.data = new_data;
572
12057
        self.offsets = new_offsets;
573
12057
    }
574

            
575
    /// Like `retain`, but passes each item's owning node index to the predicate.
576
    /// Must be called after flatten. Preserves per-node ordering.
577
    pub fn retain_with_node_index(
578
        &mut self,
579
        predicate: impl Fn(usize, &T) -> bool,
580
    ) where T: Clone {
581
        if self.offsets.is_empty() { return; }
582
        let node_count = self.offsets.len();
583
        let mut new_data = Vec::new();
584
        let mut new_offsets = Vec::with_capacity(node_count);
585
        for (node_idx, &(start, len)) in self.offsets.iter().enumerate() {
586
            let s = start as usize;
587
            let l = len as usize;
588
            let new_start = new_data.len() as u32;
589
            let slice = &self.data[s..s + l];
590
            let mut kept = 0u32;
591
            for item in slice {
592
                if predicate(node_idx, item) {
593
                    new_data.push((*item).clone());
594
                    kept += 1;
595
                }
596
            }
597
            new_offsets.push((new_start, kept));
598
        }
599
        new_data.shrink_to_fit();
600
        self.data = new_data;
601
        self.offsets = new_offsets;
602
    }
603

            
604
    /// Iterate over all nodes, yielding (node_index, &[T]) for each.
605
    /// Works in both build and flattened phases.
606
24074
    pub(crate) fn iter_node_slices(&self) -> FlatVecVecIter<'_, T> {
607
24074
        FlatVecVecIter {
608
24074
            fvv: self,
609
24074
            idx: 0,
610
24074
            count: self.len(),
611
24074
        }
612
24074
    }
613

            
614
    /// Extend this FlatVecVec with all nodes from `other` (append for DOM merge).
615
    /// Both must be in build phase, or both must be flattened.
616
456
    pub fn extend_from(&mut self, other: &mut Self) {
617
456
        if !self.offsets.is_empty() && !other.offsets.is_empty() {
618
            // Both flattened: extend flat data with offset adjustment
619
456
            let base = self.data.len() as u32;
620
456
            self.data.extend(other.data.drain(..));
621
722
            self.offsets.extend(other.offsets.drain(..).map(|(s, l)| (s + base, l)));
622
        } else {
623
            // At least one in build phase: extend build vecs
624
            self.build.extend(other.build.drain(..));
625
            // Invalidate flat data if it existed
626
            self.data.clear();
627
            self.offsets.clear();
628
        }
629
456
    }
630
}
631

            
632
/// Iterator over (node_index, &[T]) pairs from a `FlatVecVec`.
633
pub(crate) struct FlatVecVecIter<'a, T> {
634
    fvv: &'a FlatVecVec<T>,
635
    idx: usize,
636
    count: usize,
637
}
638

            
639
impl<'a, T> Iterator for FlatVecVecIter<'a, T> {
640
    type Item = (usize, &'a [T]);
641

            
642
    #[inline]
643
194442
    fn next(&mut self) -> Option<Self::Item> {
644
194442
        if self.idx >= self.count {
645
24074
            return None;
646
170368
        }
647
170368
        let i = self.idx;
648
170368
        self.idx += 1;
649
170368
        Some((i, self.fvv.get_slice(i)))
650
194442
    }
651

            
652
    fn size_hint(&self) -> (usize, Option<usize>) {
653
        let rem = self.count - self.idx;
654
        (rem, Some(rem))
655
    }
656
}
657

            
658
impl<'a, T> ExactSizeIterator for FlatVecVecIter<'a, T> {}
659

            
660
// NOTE: To avoid large memory allocations, this is a "cache" that stores all the CSS properties
661
// found in the DOM. This cache exists on a per-DOM basis, so it scales independent of how many
662
// nodes are in the DOM.
663
//
664
// If each node would carry its own CSS properties, that would unnecessarily consume memory
665
// because most nodes use the default properties or override only one or two properties.
666
//
667
// The cache can compute the property of any node at any given time, given the current node
668
// state (hover, active, focused, normal). This way we don't have to duplicate the CSS properties
669
// onto every single node and exchange them when the style changes. Two caches can be appended
670
// to each other by simply merging their NodeIds.
671
#[derive(Debug, Default, Clone, PartialEq)]
672
pub struct CssPropertyCache {
673
    // number of nodes in the current DOM
674
    pub node_count: usize,
675

            
676
    // properties that were overridden in callbacks (not specific to any node state)
677
    pub user_overridden_properties: Vec<Vec<(CssPropertyType, CssProperty)>>,
678

            
679
    // non-default CSS properties that were cascaded from the parent,
680
    // unified across all pseudo-states (Normal, Hover, Active, Focus, Dragging, DragOver).
681
    // Stored in a flat cache-friendly layout after sort_and_flatten().
682
    pub cascaded_props: FlatVecVec<StatefulCssProperty>,
683

            
684
    // non-default CSS properties that were set via a CSS file,
685
    // unified across all pseudo-states.
686
    pub css_props: FlatVecVec<StatefulCssProperty>,
687

            
688
    // Pre-resolved inherited properties (sorted Vec per node, keyed by CssPropertyType)
689
    pub computed_values: Vec<Vec<(CssPropertyType, CssPropertyWithOrigin)>>,
690

            
691
    // Compact layout cache: three-tier numeric encoding for O(1) layout lookups.
692
    // Built once after restyle + apply_ua_css + compute_inherited_values.
693
    // Non-compact properties (background, shadow, transform) use get_property_slow().
694
    pub compact_cache: Option<azul_css::compact_cache::CompactLayoutCache>,
695

            
696
    // Global CSS properties from `*` rules — shared across all nodes.
697
    // Applied during build_compact_cache_with_inheritance instead of being
698
    // cloned into each node's css_props (saves 50K×N clones).
699
    pub global_css_props: Vec<CssProperty>,
700

            
701
    /// Per-node resolved font-size, in pixels, for the `Normal`
702
    /// pseudo-state. Populated lazily on first call to
703
    /// [`crate::styled_dom::StyledDom::resolved_font_size_px`] via a
704
    /// single bottom-up DOM walk; subsequent reads are O(1) Vec
705
    /// index by `NodeId::index()`.
706
    ///
707
    /// Motivation: `get_font_size` is called ~730× per node per
708
    /// layout pass (see `AZ_PROP_COUNT=1` report — 329 629
709
    /// cascade walks on excel.html alone). Each resolution
710
    /// recursively reads the parent's font-size (for `em`) plus
711
    /// the root's font-size (for `rem`), multiplying the walk
712
    /// count. Caching the pre-resolved pixel value collapses that
713
    /// to a single `Vec<f32>` indexed lookup.
714
    pub resolved_font_sizes_px: crate::sync::OnceLock<Vec<f32>>,
715
}
716

            
717
/// Heap-size breakdown of a `CssPropertyCache`, produced by
718
/// [`CssPropertyCache::memory_breakdown`]. All values in bytes.
719
///
720
/// Primarily a diagnostic — the numbers are capacity-based and
721
/// don't chase into property-variant payloads (e.g. the `Vec`
722
/// inside a `FontFamily(...)`). Intended for "which subfield is
723
/// eating RSS" triage, not for precise accounting.
724
#[derive(Debug, Clone, Copy, Default)]
725
pub struct CssPropertyCacheBreakdown {
726
    pub node_count: usize,
727
    pub cascaded_props_bytes: usize,
728
    pub css_props_bytes: usize,
729
    pub computed_values_bytes: usize,
730
    pub user_overridden_bytes: usize,
731
    pub global_css_props_bytes: usize,
732
    pub compact_cache_bytes: usize,
733
    pub resolved_font_sizes_bytes: usize,
734
}
735

            
736
impl CssPropertyCacheBreakdown {
737
    /// Sum of all subfields.
738
    pub fn total_bytes(&self) -> usize {
739
        self.cascaded_props_bytes
740
            + self.css_props_bytes
741
            + self.computed_values_bytes
742
            + self.user_overridden_bytes
743
            + self.global_css_props_bytes
744
            + self.compact_cache_bytes
745
            + self.resolved_font_sizes_bytes
746
    }
747
}
748

            
749
impl CssPropertyCache {
750
    /// Approximate heap bytes retained by this cache, broken out by
751
    /// subfield. Used by `StyledDom::memory_breakdown` + the
752
    /// `AZ_MEM_BREAKDOWN=1` reporter. Sums capacity × element size
753
    /// for each Vec and adds a coarse allowance for the inner Vec
754
    /// headers inside `computed_values`.
755
    ///
756
    /// This is a measurement helper, not a tight bound — it doesn't
757
    /// chase into the `CssProperty` enum variants that carry their
758
    /// own `Vec`/`String` allocations (notably `FontFamily` →
759
    /// `StyleFontFamilyVec` → `Vec<StyleFontFamily>`), so the real
760
    /// heap footprint for a property-rich DOM can be 2-3× these
761
    /// numbers. Still useful for spotting gross duplication between
762
    /// the pre-compact and compact caches.
763
    pub fn memory_breakdown(&self) -> CssPropertyCacheBreakdown {
764
        let stateful_sz = core::mem::size_of::<StatefulCssProperty>();
765
        let computed_entry_sz =
766
            core::mem::size_of::<(CssPropertyType, CssPropertyWithOrigin)>();
767
        let outer_vec_sz = core::mem::size_of::<Vec<(CssPropertyType, CssPropertyWithOrigin)>>();
768

            
769
        let cascaded_bytes = self.cascaded_props.heap_bytes(stateful_sz);
770
        let css_bytes = self.css_props.heap_bytes(stateful_sz);
771

            
772
        let mut computed_bytes = self.computed_values.capacity() * outer_vec_sz;
773
        for v in &self.computed_values {
774
            computed_bytes += v.capacity() * computed_entry_sz;
775
        }
776

            
777
        let user_overridden_bytes = {
778
            let mut b = self.user_overridden_properties.capacity() * outer_vec_sz;
779
            for v in &self.user_overridden_properties {
780
                b += v.capacity()
781
                    * core::mem::size_of::<(CssPropertyType, CssProperty)>();
782
            }
783
            b
784
        };
785

            
786
        let global_bytes = self.global_css_props.capacity()
787
            * core::mem::size_of::<CssProperty>();
788

            
789
        let compact_bytes = self
790
            .compact_cache
791
            .as_ref()
792
            .map(|c| {
793
                c.tier1_enums.capacity() * 8
794
                    + c.tier2_dims.capacity() * 68
795
                    + c.tier2_cold.capacity() * 28
796
                    + c.tier2b_text.capacity() * 24
797
                    + c.prev_font_hashes.capacity() * 8
798
                    + c.font_dirty_nodes.capacity() * 8
799
            })
800
            .unwrap_or(0);
801

            
802
        let resolved_font_sizes_bytes = self
803
            .resolved_font_sizes_px
804
            .get()
805
            .map(|v| v.capacity() * core::mem::size_of::<f32>())
806
            .unwrap_or(0);
807

            
808
        CssPropertyCacheBreakdown {
809
            node_count: self.node_count,
810
            cascaded_props_bytes: cascaded_bytes,
811
            css_props_bytes: css_bytes,
812
            computed_values_bytes: computed_bytes,
813
            user_overridden_bytes,
814
            global_css_props_bytes: global_bytes,
815
            compact_cache_bytes: compact_bytes,
816
            resolved_font_sizes_bytes,
817
        }
818
    }
819

            
820
    /// Drop Normal-state properties that have compact encodings from
821
    /// `css_props` and `cascaded_props`. After `build_compact_cache_with_inheritance`,
822
    /// these are redundant — the compact cache is the source of truth for layout.
823
    /// Non-Normal entries (hover/active/focus) and non-compact properties
824
    /// (background, box-shadow, transform, etc.) are kept for `get_property_slow`.
825
12056
    pub fn prune_compact_normal_props(&mut self) {
826
        use azul_css::dynamic_selector::PseudoStateType;
827

            
828
        #[cfg(feature = "std")]
829
        {
830
        static PRUNE_DBG: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
831
12056
        let dbg = *PRUNE_DBG.get_or_init(crate::profile::memory_enabled);
832
12056
        if dbg {
833
            let mut normal_compact = 0usize;
834
            let mut normal_noncompact = 0usize;
835
            let mut nonnormal = 0usize;
836
            for i in 0..self.css_props.len() {
837
                for p in self.css_props.get_slice(i) {
838
                    if p.state != PseudoStateType::Normal {
839
                        nonnormal += 1;
840
                    } else if p.prop_type.has_compact_encoding() {
841
                        normal_compact += 1;
842
                    } else {
843
                        normal_noncompact += 1;
844
                    }
845
                }
846
            }
847
            let ssp_sz = core::mem::size_of::<StatefulCssProperty>();
848
            let mut casc_normal_compact = 0usize;
849
            let mut casc_total = 0usize;
850
            for i in 0..self.cascaded_props.len() {
851
                for p in self.cascaded_props.get_slice(i) {
852
                    casc_total += 1;
853
                    if p.state == PseudoStateType::Normal && p.prop_type.has_compact_encoding() {
854
                        casc_normal_compact += 1;
855
                    }
856
                }
857
            }
858
            eprintln!("[PRUNE] css_props: norm+compact={} norm+other={} nonnorm={} SSP={}B | cascaded: total={} norm+compact={}",
859
                normal_compact, normal_noncompact, nonnormal, ssp_sz, casc_total, casc_normal_compact);
860
12056
        }
861
        }
862

            
863
        // The compact cache stores SENTINEL for pixel-valued properties whose inner
864
        // value is Exact with a non-px metric (vh, vw, %, em, rem, calc(), ...).
865
        // Those need the slow `css_props` walk at layout time because the compact
866
        // cache has nothing usable. We must keep them here or the slow path falls
867
        // back to UA CSS and silently clobbers the author's rule.
868
195171
        let keep = |p: &StatefulCssProperty| -> bool {
869
195171
            if p.state != PseudoStateType::Normal {
870
                return true;
871
195171
            }
872
195171
            if !p.prop_type.has_compact_encoding() {
873
33026
                return true;
874
162145
            }
875
            // Compact-encoded AND Normal: drop only if the compact cache fully
876
            // captured the value (px metric, or Auto/Initial/Inherit/None).
877
162145
            if property_needs_slow_path_after_compact(&p.property) {
878
1974
                return true;
879
160171
            }
880
160171
            false
881
195171
        };
882
        // DO NOT prune css_props: regenerate_layout calls
883
        // recompute_inheritance_and_compact_cache() every frame, which REBUILDS the
884
        // compact cache from css_props (build_compact_cache_with_inheritance reads
885
        // css_props in its per-node Step 3). If we drop compact-encoded Normal props
886
        // here, that rebuild reads pruned css_props and resets those props to their
887
        // CSS-initial value — e.g. white-space:pre-wrap on a node regressed to Normal
888
        // on the 2nd (recompute) build, collapsing \n in pre-wrap text into one line
889
        // (#8, intermittently — depends on whether the recompute ran). The doc's
890
        // premise ("the compact cache is the source of truth", implying permanence)
891
        // is false given that per-frame recompute. cascaded_props is NOT read by the
892
        // rebuild (Step 1 inherits from the parent's COMPACT value, not cascaded_props),
893
        // so pruning it remains safe. TODO: re-enable css_props pruning once recompute
894
        // becomes incremental (preserve directly-set compact values instead of rebuilding).
895
12056
        if !self.cascaded_props.is_flattened() {
896
554821
            self.cascaded_props.sort_each_and_flatten(|p| (p.state, p.prop_type));
897
        }
898
12056
        self.cascaded_props.retain(keep);
899
12056
    }
900

            
901
    /// Look up a CSS property for a specific pseudo-state in a stateful property vec.
902
    /// Requires the vec to be sorted by (state, prop_type).
903
    #[inline]
904
3300500
    fn find_in_stateful<'a>(
905
3300500
        props: &'a [StatefulCssProperty],
906
3300500
        state: azul_css::dynamic_selector::PseudoStateType,
907
3300500
        prop_type: &CssPropertyType,
908
3300500
    ) -> Option<&'a CssProperty> {
909
3300500
        let key = (state, *prop_type);
910
3300500
        props.binary_search_by_key(&key, |p| (p.state, p.prop_type))
911
3300500
            .ok()
912
3300500
            .map(|idx| &props[idx].property)
913
3300500
    }
914

            
915
    /// Check if any properties exist for a specific pseudo-state in a stateful property vec.
916
    /// Requires the vec to be sorted by (state, prop_type).
917
    #[inline]
918
839867
    fn has_state_props(
919
839867
        props: &[StatefulCssProperty],
920
839867
        state: azul_css::dynamic_selector::PseudoStateType,
921
839867
    ) -> bool {
922
        // All entries with the same state are contiguous. Use partition_point
923
        // to find the first entry >= state, then check if it matches.
924
1475122
        let i = props.partition_point(|p| p.state < state);
925
839867
        i < props.len() && props[i].state == state
926
839867
    }
927

            
928
    /// Collect all property types for a specific pseudo-state.
929
950
    pub(crate) fn prop_types_for_state<'a>(
930
950
        props: &'a [StatefulCssProperty],
931
950
        state: azul_css::dynamic_selector::PseudoStateType,
932
950
    ) -> impl Iterator<Item = &'a CssPropertyType> + 'a {
933
950
        props.iter().filter(move |p| p.state == state).map(|p| &p.prop_type)
934
950
    }
935
}
936

            
937
/// Returns true if `prop`'s value cannot be fully represented in the compact
938
/// cache and therefore needs to survive `prune_compact_normal_props` so the
939
/// slow `css_props` walk can still find it at layout time.
940
///
941
/// Pixel-valued properties (margin, padding, width, height, ...) are the only
942
/// case: `Exact(pv)` with `pv.metric != Px` (vh, vw, %, em, rem, ...) encodes
943
/// to the compact cache's SENTINEL slot, which loses the value. All other
944
/// compact-encoded types (tier1 enums, colors, hashes, etc.) always round-trip
945
/// through the compact encoding.
946
162145
fn property_needs_slow_path_after_compact(prop: &CssProperty) -> bool {
947
    use azul_css::css::CssPropertyValue;
948
    use azul_css::props::{
949
        basic::length::SizeMetric,
950
        layout::{
951
            dimensions::{LayoutHeight, LayoutWidth},
952
            flex::LayoutFlexBasis,
953
        },
954
    };
955

            
956
    // `inner: PixelValue` wrapper types — check metric directly.
957
    macro_rules! check_plain {
958
        ($v:expr) => {{
959
            if let CssPropertyValue::Exact(ref inner) = $v {
960
                return inner.inner.metric != SizeMetric::Px;
961
            }
962
            false
963
        }};
964
    }
965

            
966
162145
    match prop {
967
        // LayoutWidth / LayoutHeight: enum with `Px(PixelValue)` variant.
968
        // Non-pixel variants (Auto / MinContent / MaxContent / FitContent / Calc)
969
        // are already handled by the tier1 fast path or don't exist as i16 dims.
970
        CssProperty::Width(v) => {
971
            if let CssPropertyValue::Exact(LayoutWidth::Px(pv)) = v {
972
                return pv.metric != SizeMetric::Px;
973
            }
974
            false
975
        }
976
        CssProperty::Height(v) => {
977
            if let CssPropertyValue::Exact(LayoutHeight::Px(pv)) = v {
978
                return pv.metric != SizeMetric::Px;
979
            }
980
            false
981
        }
982

            
983
        // LayoutFlexBasis: enum with `Exact(PixelValue)` variant.
984
        CssProperty::FlexBasis(v) => {
985
            if let CssPropertyValue::Exact(LayoutFlexBasis::Exact(pv)) = v {
986
                return pv.metric != SizeMetric::Px;
987
            }
988
            false
989
        }
990

            
991
        // `inner: PixelValue` wrappers
992
        CssProperty::MinWidth(v) => check_plain!(v),
993
        CssProperty::MaxWidth(v) => check_plain!(v),
994
        CssProperty::MinHeight(v) => check_plain!(v),
995
        CssProperty::MaxHeight(v) => check_plain!(v),
996
12133
        CssProperty::FontSize(v) => check_plain!(v),
997
4233
        CssProperty::PaddingTop(v) => check_plain!(v),
998
4233
        CssProperty::PaddingRight(v) => check_plain!(v),
999
4233
        CssProperty::PaddingBottom(v) => check_plain!(v),
4233
        CssProperty::PaddingLeft(v) => check_plain!(v),
7004
        CssProperty::MarginTop(v) => check_plain!(v),
6236
        CssProperty::MarginRight(v) => check_plain!(v),
7055
        CssProperty::MarginBottom(v) => check_plain!(v),
6236
        CssProperty::MarginLeft(v) => check_plain!(v),
        CssProperty::BorderTopWidth(v) => check_plain!(v),
        CssProperty::BorderRightWidth(v) => check_plain!(v),
        CssProperty::BorderBottomWidth(v) => check_plain!(v),
        CssProperty::BorderLeftWidth(v) => check_plain!(v),
        CssProperty::Top(v) => check_plain!(v),
        CssProperty::Right(v) => check_plain!(v),
        CssProperty::Bottom(v) => check_plain!(v),
        CssProperty::Left(v) => check_plain!(v),
        CssProperty::ColumnGap(v) => check_plain!(v),
        CssProperty::RowGap(v) => check_plain!(v),
        CssProperty::LetterSpacing(v) => check_plain!(v),
        CssProperty::WordSpacing(v) => check_plain!(v),
        CssProperty::TextIndent(v) => check_plain!(v),
        CssProperty::TabSize(v) => check_plain!(v),
        // All other compact-encoded types round-trip through the compact cache.
106549
        _ => false,
    }
162145
}
/// Clone a `CssProperty` WITHOUT going through its derived `Clone`. The derived clone
/// is a ~179-arm `match self { V(x) => V(x.clone()) }` that LLVM lowers to an indirect
/// HALFWORD jump table (`ldrh`-indexed). The web (remill→wasm) backend mis-lifts that
/// table, so for HEAP/Vec-bearing variants (gradients, font-family, shadows, filters,
/// transforms) the mis-dispatched clone reads wrong-sized data and the cascade traps
/// with "memory access out of bounds" (restyle → inherit → clone). Here every
/// heap-bearing variant is dispatched via single-variant `if let` — a direct
/// discriminant compare, NO jump table — and each inner `v.clone()` is the value
/// type's own clone, which lifts correctly. POD variants fall through to the derived
/// clone: correct on native, and harmless on web (a mis-dispatched discriminant 0 is
/// `CaretColor`, a `Copy` value with no heap pointer to deref). On native this function
/// is byte-for-byte equivalent to `p.clone()`.
30527
fn clone_inheritable_property(
30527
    p: &azul_css::props::property::CssProperty,
30527
) -> azul_css::props::property::CssProperty {
    use azul_css::props::property::CssProperty;
30527
    if let CssProperty::FontFamily(v) = p { return CssProperty::FontFamily(v.clone()); }
22967
    if let CssProperty::BackgroundContent(v) = p { return CssProperty::BackgroundContent(v.clone()); }
22967
    if let CssProperty::BackgroundPosition(v) = p { return CssProperty::BackgroundPosition(v.clone()); }
22967
    if let CssProperty::BackgroundSize(v) = p { return CssProperty::BackgroundSize(v.clone()); }
22967
    if let CssProperty::BackgroundRepeat(v) = p { return CssProperty::BackgroundRepeat(v.clone()); }
22967
    if let CssProperty::BoxShadowLeft(v) = p { return CssProperty::BoxShadowLeft(v.clone()); }
22967
    if let CssProperty::BoxShadowRight(v) = p { return CssProperty::BoxShadowRight(v.clone()); }
22967
    if let CssProperty::BoxShadowTop(v) = p { return CssProperty::BoxShadowTop(v.clone()); }
22967
    if let CssProperty::BoxShadowBottom(v) = p { return CssProperty::BoxShadowBottom(v.clone()); }
22967
    if let CssProperty::TextShadow(v) = p { return CssProperty::TextShadow(v.clone()); }
22967
    if let CssProperty::ScrollbarTrack(v) = p { return CssProperty::ScrollbarTrack(v.clone()); }
22967
    if let CssProperty::ScrollbarThumb(v) = p { return CssProperty::ScrollbarThumb(v.clone()); }
22967
    if let CssProperty::ScrollbarButton(v) = p { return CssProperty::ScrollbarButton(v.clone()); }
22967
    if let CssProperty::ScrollbarCorner(v) = p { return CssProperty::ScrollbarCorner(v.clone()); }
22967
    if let CssProperty::ScrollbarResizer(v) = p { return CssProperty::ScrollbarResizer(v.clone()); }
22967
    if let CssProperty::Transform(v) = p { return CssProperty::Transform(v.clone()); }
22967
    if let CssProperty::Filter(v) = p { return CssProperty::Filter(v.clone()); }
22967
    if let CssProperty::BackdropFilter(v) = p { return CssProperty::BackdropFilter(v.clone()); }
22967
    if let CssProperty::Content(v) = p { return CssProperty::Content(v.clone()); }
22967
    if let CssProperty::HyphenationLanguage(v) = p { return CssProperty::HyphenationLanguage(v.clone()); }
22967
    if let CssProperty::Cursor(v) = p { return CssProperty::Cursor(v.clone()); }
19295
    p.clone()
30527
}
impl CssPropertyCache {
    /// Match CSS selectors to nodes and populate css_props.
    /// Returns tag IDs for hit-testing. If compact_cache is available,
    /// uses it for fast display/overflow checks; otherwise falls back to slow path.
    #[must_use]
12037
    pub fn restyle(
12037
        &mut self,
12037
        css: &mut Css,
12037
        node_data: &NodeDataContainerRef<NodeData>,
12037
        node_hierarchy: &NodeHierarchyItemVec,
12037
        non_leaf_nodes: &ParentWithNodeDepthVec,
12037
        html_tree: &NodeDataContainerRef<CascadeInfo>,
12037
    ) -> Vec<TagIdToNodeIdMapping> {
        use azul_css::{
            css::{CssDeclaration, CssPathPseudoSelector::*},
            props::layout::LayoutDisplay,
        };
12037
        let css_is_empty = css.is_empty();
12037
        if !css_is_empty {
8099
            css.sort_by_specificity();
            // Separate CSS rules into "global only" (just `*`) vs "has specific selector".
            // Global-only rules apply to ALL nodes — push directly into css_props
            // without per-node selector matching (avoids m×n for these rules).
            // Specific rules still go through matches_html_element per-node.
            use azul_css::css::{CssPathSelector, CssRuleBlock};
8099
            let mut global_only_rules: Vec<&CssRuleBlock> = Vec::new();
8099
            let mut specific_rules: Vec<&CssRuleBlock> = Vec::new();
36264
            for rule in css.rules() {
36264
                let selectors = rule.path.selectors.as_ref();
36264
                let is_global_only = selectors.len() == 1
10089
                    && matches!(selectors.first(), Some(CssPathSelector::Global));
36264
                if is_global_only {
1536
                    global_only_rules.push(rule);
34728
                } else {
34728
                    specific_rules.push(rule);
34728
                }
            }
            // Clear all css_props before assigning
59019
            for entry in self.css_props.build_iter_mut() { entry.clear(); }
            use azul_css::dynamic_selector::PseudoStateType;
            // Collect global-only rule declarations ONCE (not per-node).
            // These are stored in self.global_css_props and applied during
            // build_compact_cache_with_inheritance for each node, avoiding
            // 50K × N clones into per-node css_props Vecs.
8099
            self.global_css_props.clear();
9635
            for rule in &global_only_rules {
1536
                if crate::style::rule_ends_with(&rule.path, None) {
11779
                    for d in rule.declarations.iter() {
11779
                        if let CssDeclaration::Static(s) = d {
11779
                            self.global_css_props.push(s.clone());
11779
                        }
                    }
                }
            }
            // Phase 2: Match specific rules per-node (only non-global rules)
8099
            if !specific_rules.is_empty() {
            // Per-node "which declarations match" lists are built as
            // `(rule_idx, decl_idx)` pairs — 4 bytes per entry instead of
            // cloning a 140-byte `CssProperty`. The clone only happens at
            // the final push_to step, so the transient peak is ~35× smaller.
            //
            // rule_idx indexes into `specific_rules` (Vec<&CssRuleBlock>),
            // decl_idx indexes into `rule.declarations.as_slice()`. Both
            // fit in u16 since real stylesheets have far fewer than 65k
            // rules and declarations per rule.
            macro_rules! filter_rules {($expected_pseudo_selector:expr, $node_id:expr) => {{
                let mut out: Vec<(u16, u16)> = Vec::new();
                for (rule_idx, rule_block) in specific_rules.iter().enumerate() {
                    if !crate::style::rule_ends_with(&rule_block.path, $expected_pseudo_selector) {
                        continue;
                    }
                    if !crate::style::matches_html_element(
                        &rule_block.path,
                        $node_id,
                        &node_hierarchy.as_container(),
                        &node_data,
                        &html_tree,
                        $expected_pseudo_selector,
                    ) {
                        continue;
                    }
                    for (decl_idx, decl) in rule_block.declarations.as_slice().iter().enumerate() {
                        if matches!(decl, CssDeclaration::Static(_)) {
                            out.push((rule_idx as u16, decl_idx as u16));
                        }
                    }
                }
                out
            }};}
            // Pre-check which pseudo-states have any matching rules at all.
            // This avoids iterating 50K nodes for pseudo-states with zero rules
            // (common: most stylesheets have no :hover/:focus/:active rules).
8010
            let has_normal = specific_rules.iter().any(|r| crate::style::rule_ends_with(&r.path, None));
33963
            let has_hover = specific_rules.iter().any(|r| crate::style::rule_ends_with(&r.path, Some(Hover)));
34728
            let has_active = specific_rules.iter().any(|r| crate::style::rule_ends_with(&r.path, Some(Active)));
34728
            let has_focus = specific_rules.iter().any(|r| crate::style::rule_ends_with(&r.path, Some(Focus)));
34728
            let has_dragging = specific_rules.iter().any(|r| crate::style::rule_ends_with(&r.path, Some(Dragging)));
34728
            let has_drag_over = specific_rules.iter().any(|r| crate::style::rule_ends_with(&r.path, Some(DragOver)));
            macro_rules! collect_and_assign {
                ($pseudo:expr, $state:expr, $has_any:expr) => {
                    if $has_any {
                        let indices: NodeDataContainer<(NodeId, Vec<(u16, u16)>)> = node_data
60894
                            .transform_nodeid_optional(|node_id| {
779145
                                let r = filter_rules!($pseudo, node_id);
60894
                                if r.is_empty() { None } else { Some((node_id, r)) }
60894
                            });
                        for (n, pairs) in indices.internal.into_iter() {
                            for (rule_idx, decl_idx) in pairs {
                                let decl = &specific_rules[rule_idx as usize]
                                    .declarations
                                    .as_slice()[decl_idx as usize];
                                if let CssDeclaration::Static(prop) = decl {
                                    self.css_props.push_to(n.index(), StatefulCssProperty {
                                        state: $state,
                                        prop_type: prop.get_type(),
                                        property: prop.clone(),
                                    });
                                }
                            }
                        }
                    }
                };
            }
296729
            collect_and_assign!(None, PseudoStateType::Normal, has_normal);
8010
            collect_and_assign!(Some(Hover), PseudoStateType::Hover, has_hover);
8010
            collect_and_assign!(Some(Active), PseudoStateType::Active, has_active);
8010
            collect_and_assign!(Some(Focus), PseudoStateType::Focus, has_focus);
8010
            collect_and_assign!(Some(Dragging), PseudoStateType::Dragging, has_dragging);
8010
            collect_and_assign!(Some(DragOver), PseudoStateType::DragOver, has_drag_over);
89
            } // end if !specific_rules.is_empty()
3938
        }
        // Inheritance: Inherit all values of the parent to the children, but
        // only if the property is inheritable and isn't yet set
49103
        for ParentWithNodeDepth { depth: _, node_id } in non_leaf_nodes.iter() {
49103
            let parent_id = match node_id.into_crate_internal() {
49103
                Some(s) => s,
                None => continue,
            };
            use azul_css::dynamic_selector::{DynamicSelector, PseudoStateType};
49103
            let all_states = [
49103
                PseudoStateType::Normal,
49103
                PseudoStateType::Hover,
49103
                PseudoStateType::Active,
49103
                PseudoStateType::Focus,
49103
                PseudoStateType::Dragging,
49103
                PseudoStateType::DragOver,
49103
            ];
343721
            for &state in &all_states {
                // 1. Inherit inline CSS properties from parent for this pseudo-state
294618
                let parent_inheritable_inline: Vec<(CssPropertyType, CssProperty)> = node_data[parent_id]
294618
                    .style
294618
                    .iter_inline_properties()
294618
                    .filter(|(_prop, conds)| {
3876
                        let conditions = conds.as_slice();
3876
                        if conditions.is_empty() {
3876
                            state == PseudoStateType::Normal
                        } else {
                            conditions.iter().all(|c| {
                                matches!(c, DynamicSelector::PseudoState(s) if *s == state)
                            })
                        }
3876
                    })
294618
                    .map(|(prop, _)| prop)
294618
                    .filter(|prop| prop.get_type().is_inheritable())
294618
                    .map(|p| (p.get_type(), clone_inheritable_property(p)))
294618
                    .collect();
                // 2. Inherit CSS stylesheet properties from parent for this pseudo-state
294618
                let parent_inheritable_css: Vec<(CssPropertyType, CssProperty)> = if !css_is_empty {
185610
                    self.css_props.get_slice(parent_id.index())
185610
                        .iter()
1438722
                        .filter(|p| p.state == state && p.prop_type.is_inheritable())
185610
                        .map(|p| (p.prop_type, clone_inheritable_property(&p.property)))
185610
                        .collect()
                } else {
109008
                    Vec::new()
                };
                // 3. Inherit cascaded properties from parent for this pseudo-state
294618
                let parent_inheritable_cascaded: Vec<(CssPropertyType, CssProperty)> =
294618
                    self.cascaded_props.get_slice(parent_id.index())
294618
                        .iter()
294618
                        .filter(|p| p.state == state && p.prop_type.is_inheritable())
294618
                        .map(|p| (p.prop_type, clone_inheritable_property(&p.property)))
294618
                        .collect();
                // Combine all inheritable props (inline first = strongest, cascaded last)
                // Only insert if child doesn't already have that (state, prop_type) combo
294618
                if parent_inheritable_inline.is_empty()
294048
                    && parent_inheritable_css.is_empty()
286699
                    && parent_inheritable_cascaded.is_empty()
                {
284668
                    continue;
9950
                }
13826
                for child_id in parent_id.az_children(&node_hierarchy.as_container()) {
13807
                    let child_vec = self.cascaded_props.build_mut(child_id.index());
40912
                    for (prop_type, prop_value) in parent_inheritable_inline
13807
                        .iter()
13807
                        .chain(parent_inheritable_css.iter())
13807
                        .chain(parent_inheritable_cascaded.iter())
                    {
                        // or_insert: only insert if child doesn't already have this (state, prop_type)
46471
                        if !child_vec.iter().any(|p| p.state == state && p.prop_type == *prop_type) {
34704
                            child_vec.push(StatefulCssProperty {
34704
                                state,
34704
                                prop_type: *prop_type,
34704
                                property: prop_value.clone(),
34704
                            });
34704
                        }
                    }
                }
            }
        }
        // Sort css_props by (state, prop_type) for binary search lookups,
        // then flatten into contiguous memory for cache-friendly reads.
2500046
        self.css_props.sort_each_and_flatten(|p| (p.state, p.prop_type));
12037
        self.generate_tag_ids(node_data, node_hierarchy)
12037
    }
    /// Generate hit-test tag IDs for nodes that need event handling.
    /// Uses compact cache (if available) for fast display/overflow reads.
    /// Can be called separately after build_compact_cache_with_inheritance.
24074
    pub fn generate_tag_ids(
24074
        &self,
24074
        node_data: &NodeDataContainerRef<NodeData>,
24074
        node_hierarchy: &NodeHierarchyItemVec,
24074
    ) -> Vec<TagIdToNodeIdMapping> {
        // Tag ID generation: determine which nodes need hit-test tags for
        // hover/click/scroll events. Uses compact cache for display/overflow
        // checks instead of get_property_slow (which searches 6 data structures).
        use azul_css::compact_cache::{
            DISPLAY_SHIFT, DISPLAY_MASK,
            OVERFLOW_X_SHIFT, OVERFLOW_Y_SHIFT, OVERFLOW_MASK,
        };
24074
        let compact_cache = self.compact_cache.as_ref();
24074
        let node_data_container = &node_data.internal;
24074
        let tag_ids = node_data
24074
            .internal
24074
            .iter()
24074
            .enumerate()
170368
            .filter_map(|(node_idx, node_data)| {
170368
                let node_id = NodeId::new(node_idx);
170368
                let should_auto_insert_tabindex = node_data
170368
                    .get_callbacks()
170368
                    .iter()
170368
                    .any(|cb| cb.event.is_focus_callback());
170368
                let tab_index = match node_data.get_tab_index() {
816
                    Some(s) => Some(s),
                    None => {
169552
                        if should_auto_insert_tabindex {
                            Some(TabIndex::Auto)
                        } else {
169552
                            None
                        }
                    }
                };
170368
                let mut need_tag = false;
                loop {
                    // display:none check — read directly from compact tier1 (fast u64 read)
170368
                    if let Some(cc) = compact_cache.as_ref() {
85184
                        let t1 = cc.tier1_enums[node_idx];
85184
                        let display_val = ((t1 >> DISPLAY_SHIFT) & DISPLAY_MASK) as u8;
85184
                        if display_val == 4 { break; } // 4 = LayoutDisplay::None (new encoding)
85184
                    }
170317
                    if node_data.has_context_menu() || node_data.get_context_menu().is_some() {
                        need_tag = true; break;
170317
                    }
170317
                    if tab_index.is_some() { need_tag = true; break; }
                    // Pseudo-state property checks (hover/active/focus/dragging/drag-over)
                    {
                        use azul_css::dynamic_selector::{DynamicSelector, PseudoStateType};
841425
                        let has_pseudo = |state: PseudoStateType| -> bool {
841425
                            node_data.style.iter_inline_properties().any(|(_p, conds)| {
11362
                                conds.as_slice().iter().any(|c|
3192
                                    matches!(c, DynamicSelector::PseudoState(s) if *s == state)
                                )
839867
                            }) || Self::has_state_props(self.css_props.get_slice(node_idx), state)
841425
                        };
169501
                        if has_pseudo(PseudoStateType::Hover)
168019
                            || has_pseudo(PseudoStateType::Active)
168019
                            || has_pseudo(PseudoStateType::Focus)
167943
                            || has_pseudo(PseudoStateType::Dragging)
167943
                            || has_pseudo(PseudoStateType::DragOver)
                        {
1558
                            need_tag = true; break;
167943
                        }
                    }
                    // Non-window callbacks
167943
                    let has_non_window_cb = !node_data.get_callbacks().is_empty()
3774
                        && !node_data.get_callbacks().iter().all(|cb| cb.event.is_window_callback());
167943
                    if has_non_window_cb { need_tag = true; break; }
                    // Cursor check — read from cached css_props or inline style.
164169
                    if self.css_props.get_slice(node_idx).iter().any(|p|
501556
                        p.state == azul_css::dynamic_selector::PseudoStateType::Normal
501556
                        && p.prop_type == azul_css::props::property::CssPropertyType::Cursor
158661
                    ) || node_data.style.iter_inline_properties().any(|(p, _)|
1292
                        p.get_type() == azul_css::props::property::CssPropertyType::Cursor
                    ) {
5508
                        need_tag = true; break;
158661
                    }
                    // Overflow scroll check — read from compact tier1
158661
                    if let Some(cc) = compact_cache.as_ref() {
79305
                        let t1 = cc.tier1_enums[node_idx];
79305
                        let ox = ((t1 >> OVERFLOW_X_SHIFT) & OVERFLOW_MASK) as u8;
79305
                        let oy = ((t1 >> OVERFLOW_Y_SHIFT) & OVERFLOW_MASK) as u8;
                        // 2 = Scroll, 3 = Auto in layout_overflow_to_u8 (new encoding)
79305
                        if ox == 2 || ox == 3 || oy == 2 || oy == 3 {
                            need_tag = true; break;
79305
                        }
79356
                    }
                    // Selectable text check
                    {
                        use crate::dom::NodeType;
158661
                        let hier = node_hierarchy.as_container()[node_id];
158661
                        let mut has_text = false;
158661
                        if let Some(first_child) = hier.first_child_id(node_id) {
85026
                            let mut child_id = Some(first_child);
148906
                            while let Some(cid) = child_id {
102486
                                if matches!(node_data_container[cid.index()].get_node_type(), NodeType::Text(_)) {
38606
                                    has_text = true; break;
63880
                                }
63880
                                child_id = node_hierarchy.as_container()[cid].next_sibling_id();
                            }
73635
                        }
158661
                        if has_text { need_tag = true; break; }
                    }
120055
                    break;
                }
170368
                if !need_tag {
120106
                    None
                } else {
50262
                    Some(TagIdToNodeIdMapping {
50262
                        tag_id: TagId::from_crate_internal(TagId::unique()),
50262
                        node_id: NodeHierarchyItemId::from_crate_internal(Some(node_id)),
50262
                        tab_index: tab_index.into(),
50262
                    })
                }
170368
            })
24074
            .collect::<Vec<_>>();
24074
        tag_ids
24074
    }
    pub fn get_computed_css_style_string(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> String {
        let mut s = String::new();
        if let Some(p) = self.get_background_content(&node_data, node_id, node_state) {
            s.push_str(&format!("background: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_background_position(&node_data, node_id, node_state) {
            s.push_str(&format!("background-position: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_background_size(&node_data, node_id, node_state) {
            s.push_str(&format!("background-size: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_background_repeat(&node_data, node_id, node_state) {
            s.push_str(&format!("background-repeat: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_font_size(&node_data, node_id, node_state) {
            s.push_str(&format!("font-size: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_font_family(&node_data, node_id, node_state) {
            s.push_str(&format!("font-family: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_text_color(&node_data, node_id, node_state) {
            s.push_str(&format!("color: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_text_align(&node_data, node_id, node_state) {
            s.push_str(&format!("text-align: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_line_height(&node_data, node_id, node_state) {
            s.push_str(&format!("line-height: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_letter_spacing(&node_data, node_id, node_state) {
            s.push_str(&format!("letter-spacing: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_word_spacing(&node_data, node_id, node_state) {
            s.push_str(&format!("word-spacing: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_tab_size(&node_data, node_id, node_state) {
            s.push_str(&format!("tab-size: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_cursor(&node_data, node_id, node_state) {
            s.push_str(&format!("cursor: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_box_shadow_left(&node_data, node_id, node_state) {
            s.push_str(&format!(
                "-azul-box-shadow-left: {};",
                p.get_css_value_fmt()
            ));
        }
        if let Some(p) = self.get_box_shadow_right(&node_data, node_id, node_state) {
            s.push_str(&format!(
                "-azul-box-shadow-right: {};",
                p.get_css_value_fmt()
            ));
        }
        if let Some(p) = self.get_box_shadow_top(&node_data, node_id, node_state) {
            s.push_str(&format!("-azul-box-shadow-top: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_box_shadow_bottom(&node_data, node_id, node_state) {
            s.push_str(&format!(
                "-azul-box-shadow-bottom: {};",
                p.get_css_value_fmt()
            ));
        }
        if let Some(p) = self.get_border_top_color(&node_data, node_id, node_state) {
            s.push_str(&format!("border-top-color: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_left_color(&node_data, node_id, node_state) {
            s.push_str(&format!("border-left-color: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_right_color(&node_data, node_id, node_state) {
            s.push_str(&format!("border-right-color: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_bottom_color(&node_data, node_id, node_state) {
            s.push_str(&format!("border-bottom-color: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_top_style(&node_data, node_id, node_state) {
            s.push_str(&format!("border-top-style: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_left_style(&node_data, node_id, node_state) {
            s.push_str(&format!("border-left-style: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_right_style(&node_data, node_id, node_state) {
            s.push_str(&format!("border-right-style: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_bottom_style(&node_data, node_id, node_state) {
            s.push_str(&format!("border-bottom-style: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_top_left_radius(&node_data, node_id, node_state) {
            s.push_str(&format!(
                "border-top-left-radius: {};",
                p.get_css_value_fmt()
            ));
        }
        if let Some(p) = self.get_border_top_right_radius(&node_data, node_id, node_state) {
            s.push_str(&format!(
                "border-top-right-radius: {};",
                p.get_css_value_fmt()
            ));
        }
        if let Some(p) = self.get_border_bottom_left_radius(&node_data, node_id, node_state) {
            s.push_str(&format!(
                "border-bottom-left-radius: {};",
                p.get_css_value_fmt()
            ));
        }
        if let Some(p) = self.get_border_bottom_right_radius(&node_data, node_id, node_state) {
            s.push_str(&format!(
                "border-bottom-right-radius: {};",
                p.get_css_value_fmt()
            ));
        }
        if let Some(p) = self.get_opacity(&node_data, node_id, node_state) {
            s.push_str(&format!("opacity: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_transform(&node_data, node_id, node_state) {
            s.push_str(&format!("transform: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_transform_origin(&node_data, node_id, node_state) {
            s.push_str(&format!("transform-origin: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_perspective_origin(&node_data, node_id, node_state) {
            s.push_str(&format!("perspective-origin: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_backface_visibility(&node_data, node_id, node_state) {
            s.push_str(&format!("backface-visibility: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_hyphens(&node_data, node_id, node_state) {
            s.push_str(&format!("hyphens: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_direction(&node_data, node_id, node_state) {
            s.push_str(&format!("direction: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_unicode_bidi(&node_data, node_id, node_state) {
            s.push_str(&format!("unicode-bidi: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_text_box_trim(&node_data, node_id, node_state) {
            s.push_str(&format!("text-box-trim: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_text_box_edge(&node_data, node_id, node_state) {
            s.push_str(&format!("text-box-edge: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_dominant_baseline(&node_data, node_id, node_state) {
            s.push_str(&format!("dominant-baseline: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_alignment_baseline(&node_data, node_id, node_state) {
            s.push_str(&format!("alignment-baseline: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_initial_letter_align(&node_data, node_id, node_state) {
            s.push_str(&format!("initial-letter-align: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_initial_letter_wrap(&node_data, node_id, node_state) {
            s.push_str(&format!("initial-letter-wrap: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_scrollbar_gutter(&node_data, node_id, node_state) {
            s.push_str(&format!("scrollbar-gutter: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_overflow_clip_margin(&node_data, node_id, node_state) {
            s.push_str(&format!("overflow-clip-margin: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_clip(&node_data, node_id, node_state) {
            s.push_str(&format!("clip: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_white_space(&node_data, node_id, node_state) {
            s.push_str(&format!("white-space: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_display(&node_data, node_id, node_state) {
            s.push_str(&format!("display: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_float(&node_data, node_id, node_state) {
            s.push_str(&format!("float: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_box_sizing(&node_data, node_id, node_state) {
            s.push_str(&format!("box-sizing: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_width(&node_data, node_id, node_state) {
            s.push_str(&format!("width: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_height(&node_data, node_id, node_state) {
            s.push_str(&format!("height: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_min_width(&node_data, node_id, node_state) {
            s.push_str(&format!("min-width: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_min_height(&node_data, node_id, node_state) {
            s.push_str(&format!("min-height: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_max_width(&node_data, node_id, node_state) {
            s.push_str(&format!("max-width: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_max_height(&node_data, node_id, node_state) {
            s.push_str(&format!("max-height: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_position(&node_data, node_id, node_state) {
            s.push_str(&format!("position: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_top(&node_data, node_id, node_state) {
            s.push_str(&format!("top: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_bottom(&node_data, node_id, node_state) {
            s.push_str(&format!("bottom: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_right(&node_data, node_id, node_state) {
            s.push_str(&format!("right: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_left(&node_data, node_id, node_state) {
            s.push_str(&format!("left: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_padding_top(&node_data, node_id, node_state) {
            s.push_str(&format!("padding-top: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_padding_bottom(&node_data, node_id, node_state) {
            s.push_str(&format!("padding-bottom: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_padding_left(&node_data, node_id, node_state) {
            s.push_str(&format!("padding-left: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_padding_right(&node_data, node_id, node_state) {
            s.push_str(&format!("padding-right: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_margin_top(&node_data, node_id, node_state) {
            s.push_str(&format!("margin-top: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_margin_bottom(&node_data, node_id, node_state) {
            s.push_str(&format!("margin-bottom: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_margin_left(&node_data, node_id, node_state) {
            s.push_str(&format!("margin-left: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_margin_right(&node_data, node_id, node_state) {
            s.push_str(&format!("margin-right: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_top_width(&node_data, node_id, node_state) {
            s.push_str(&format!("border-top-width: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_left_width(&node_data, node_id, node_state) {
            s.push_str(&format!("border-left-width: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_right_width(&node_data, node_id, node_state) {
            s.push_str(&format!("border-right-width: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_border_bottom_width(&node_data, node_id, node_state) {
            s.push_str(&format!("border-bottom-width: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_overflow_x(&node_data, node_id, node_state) {
            s.push_str(&format!("overflow-x: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_overflow_y(&node_data, node_id, node_state) {
            s.push_str(&format!("overflow-y: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_flex_direction(&node_data, node_id, node_state) {
            s.push_str(&format!("flex-direction: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_flex_wrap(&node_data, node_id, node_state) {
            s.push_str(&format!("flex-wrap: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_flex_grow(&node_data, node_id, node_state) {
            s.push_str(&format!("flex-grow: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_flex_shrink(&node_data, node_id, node_state) {
            s.push_str(&format!("flex-shrink: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_justify_content(&node_data, node_id, node_state) {
            s.push_str(&format!("justify-content: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_align_items(&node_data, node_id, node_state) {
            s.push_str(&format!("align-items: {};", p.get_css_value_fmt()));
        }
        if let Some(p) = self.get_align_content(&node_data, node_id, node_state) {
            s.push_str(&format!("align-content: {};", p.get_css_value_fmt()));
        }
        s
    }
}
#[repr(C)]
#[derive(Debug, PartialEq, Clone)]
pub struct CssPropertyCachePtr {
    // `ManuallyDrop` so the owned `Box` is freed ONLY by our `Drop` (gated on
    // `run_destructor`), never by drop-glue. The codegen Az wrapper (AzStyledDom)
    // nests an AzCssPropertyCachePtr field whose own `Drop` re-runs
    // `_delete` -> `drop_in_place::<CssPropertyCachePtr>` on the SAME bytes; with a
    // bare `Box` the glue freed it a second time -> double free. Layout is
    // unchanged (one pointer), so the AzCssPropertyCachePtr<->CssPropertyCachePtr
    // FFI transmute stays valid. Matches the GlContextPtr / InstantPtr convention.
    pub ptr: ManuallyDrop<Box<CssPropertyCache>>,
    pub run_destructor: bool,
}
impl CssPropertyCachePtr {
12088
    pub fn new(cache: CssPropertyCache) -> Self {
12088
        Self {
12088
            ptr: ManuallyDrop::new(Box::new(cache)),
12088
            run_destructor: true,
12088
        }
12088
    }
3
    pub fn downcast_mut<'a>(&'a mut self) -> &'a mut CssPropertyCache {
3
        &mut **self.ptr
3
    }
}
impl Drop for CssPropertyCachePtr {
12139
    fn drop(&mut self) {
        // First drop (run_destructor still true) frees the Box and clears the flag in
        // the shared bytes; the codegen's redundant second drop sees false -> no-op.
12139
        if self.run_destructor {
12139
            self.run_destructor = false;
12139
            unsafe {
12139
                ManuallyDrop::drop(&mut self.ptr);
12139
            }
        }
12139
    }
}
impl CssPropertyCache {
12145
    pub fn empty(node_count: usize) -> Self {
12145
        Self {
12145
            node_count,
12145
            user_overridden_properties: Vec::new(),
12145

            
12145
            cascaded_props: FlatVecVec::new(node_count),
12145
            css_props: FlatVecVec::new(node_count),
12145

            
12145
            computed_values: Vec::new(),
12145
            compact_cache: None,
12145
            global_css_props: Vec::new(),
12145
            resolved_font_sizes_px: crate::sync::OnceLock::new(),
12145
        }
12145
    }
    /// Clear the lazily-populated font-size cache. Call after any
    /// mutation that could change resolved font-sizes (restyle,
    /// DOM mutation, `append`, etc.). The next
    /// [`crate::styled_dom::StyledDom::resolved_font_size_px`] call
    /// repopulates via a single bottom-up tree walk.
    pub fn invalidate_resolved_font_sizes(&mut self) {
        self.resolved_font_sizes_px = crate::sync::OnceLock::new();
    }
228
    pub fn append(&mut self, other: &mut Self) {
228
        self.user_overridden_properties.extend(other.user_overridden_properties.drain(..));
228
        self.cascaded_props.extend_from(&mut other.cascaded_props);
228
        self.css_props.extend_from(&mut other.css_props);
228
        self.computed_values.extend(other.computed_values.drain(..));
228
        self.node_count += other.node_count;
        // Indices shifted — invalidate the font-size cache too.
228
        self.resolved_font_sizes_px = crate::sync::OnceLock::new();
        // Invalidate compact cache since node IDs shifted
228
        self.compact_cache = None;
228
    }
    pub fn is_horizontal_overflow_visible(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> bool {
        self.get_overflow_x(node_data, node_id, node_state)
            .and_then(|p| p.get_property_or_default())
            .unwrap_or_default()
            .is_overflow_visible()
    }
    pub fn is_vertical_overflow_visible(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> bool {
        self.get_overflow_y(node_data, node_id, node_state)
            .and_then(|p| p.get_property_or_default())
            .unwrap_or_default()
            .is_overflow_visible()
    }
    pub fn is_horizontal_overflow_hidden(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> bool {
        self.get_overflow_x(node_data, node_id, node_state)
            .and_then(|p| p.get_property_or_default())
            .unwrap_or_default()
            .is_overflow_hidden()
    }
    pub fn is_vertical_overflow_hidden(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> bool {
        self.get_overflow_y(node_data, node_id, node_state)
            .and_then(|p| p.get_property_or_default())
            .unwrap_or_default()
            .is_overflow_hidden()
    }
1530
    pub fn get_text_color_or_default(
1530
        &self,
1530
        node_data: &NodeData,
1530
        node_id: &NodeId,
1530
        node_state: &StyledNodeState,
1530
    ) -> StyleTextColor {
        use azul_css::defaults::DEFAULT_TEXT_COLOR;
1530
        self.get_text_color(node_data, node_id, node_state)
1530
            .and_then(|fs| fs.get_property().cloned())
1530
            .unwrap_or(DEFAULT_TEXT_COLOR)
1530
    }
    /// Returns the font family of the node, or the default font family if none is set.
    pub fn get_font_id_or_default(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> StyleFontFamilyVec {
        use azul_css::defaults::DEFAULT_FONT_ID;
        let default_font_id = vec![StyleFontFamily::System(AzString::from_const_str(
            DEFAULT_FONT_ID,
        ))]
        .into();
        let font_family_opt = self.get_font_family(node_data, node_id, node_state);
        font_family_opt
            .as_ref()
            .and_then(|family| Some(family.get_property()?.clone()))
            .unwrap_or(default_font_id)
    }
    pub fn get_font_size_or_default(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> StyleFontSize {
        use azul_css::defaults::DEFAULT_FONT_SIZE;
        self.get_font_size(node_data, node_id, node_state)
            .and_then(|fs| fs.get_property().cloned())
            .unwrap_or(DEFAULT_FONT_SIZE)
    }
    pub fn has_border(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> bool {
        self.get_border_left_width(node_data, node_id, node_state)
            .is_some()
            || self
                .get_border_right_width(node_data, node_id, node_state)
                .is_some()
            || self
                .get_border_top_width(node_data, node_id, node_state)
                .is_some()
            || self
                .get_border_bottom_width(node_data, node_id, node_state)
                .is_some()
    }
    pub fn has_box_shadow(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> bool {
        self.get_box_shadow_left(node_data, node_id, node_state)
            .is_some()
            || self
                .get_box_shadow_right(node_data, node_id, node_state)
                .is_some()
            || self
                .get_box_shadow_top(node_data, node_id, node_state)
                .is_some()
            || self
                .get_box_shadow_bottom(node_data, node_id, node_state)
                .is_some()
    }
1660545
    pub fn get_property<'a>(
1660545
        &'a self,
1660545
        node_data: &'a NodeData,
1660545
        node_id: &NodeId,
1660545
        node_state: &StyledNodeState,
1660545
        css_property_type: &CssPropertyType,
1660545
    ) -> Option<&CssProperty> {
        // Thread-local counter of cascade walks, broken down by
        // property type. Drain with `drain_css_prop_counts` (free
        // fn below) when `AZ_PROP_COUNT=1` is set to see which
        // properties dominate the cold layout path.
        //
        // Env check is read ONCE at process start and cached in a
        // `OnceLock<bool>`. Before this, the env check ran per
        // `get_property` call — and the function fires 710k+ times
        // per cold layout on excel.html. `std::env::var_os` takes
        // ~100 ns per call on macOS (env lock + hashmap lookup), so
        // the naive check added ~70 ms of pure noise to every
        // single layout, regardless of whether the env var was set.
        // Using a one-time cached bool removes that overhead.
        //
        // `no_std` builds have no thread-locals / env, so the profiling
        // counter is compiled out entirely.
        #[cfg(feature = "std")]
        {
            static PROP_COUNT_ENABLED: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1660545
            let enabled = *PROP_COUNT_ENABLED.get_or_init(crate::profile::cascade_enabled);
1660545
            if enabled {
                // `try_with` (not `with`): the lifted-to-wasm web backend has no
                // real TLS, so `with` would hit `panic_access_error` (the layout
                // path reads CSS props via these getters → would trap). `try_with`
                // returns Err and we skip the profiling-only increment (and its
                // inner Mutex-guarded label table). Desktop behaviour unchanged —
                // when the env var is unset the whole block is gated off anyway.
                let _ = PROP_COUNTS.try_with(|c| {
                    *c.borrow_mut()
                        .entry(Self::css_prop_type_label(css_property_type))
                        .or_insert(0) += 1;
                });
1660545
            }
        }
        // Always use full cascade resolution.
        // Tier 1/2/2b handle layout-hot properties via direct typed getters.
        // This path is only used for paint-time reads (background, shadow, etc.)
1660545
        self.get_property_slow(node_data, node_id, node_state, css_property_type)
1660545
    }
    #[cfg(feature = "std")]
    fn css_prop_type_label(t: &CssPropertyType) -> &'static str {
        // Intern Debug-format labels under a mutex-guarded map so
        // we leak at most one `&'static str` per distinct
        // `CssPropertyType` variant (bounded at ≤ 178 total). Only
        // triggered when `AZ_PROP_COUNT=1`, so zero cost normally.
        use std::sync::{Mutex, OnceLock};
        static TABLE: OnceLock<Mutex<std::collections::HashMap<CssPropertyType, &'static str>>> =
            OnceLock::new();
        let m = TABLE.get_or_init(|| Mutex::new(std::collections::HashMap::new()));
        let mut g = m.lock().expect("AZ_PROP_COUNT label table poisoned");
        if let Some(s) = g.get(t) {
            return *s;
        }
        let s: String = std::format!("{:?}", t);
        let leaked: &'static str = std::boxed::Box::leak(s.into_boxed_str());
        g.insert(*t, leaked);
        leaked
    }
    /// Full cascade resolution for any CSS property type.
    /// Walks all cascade layers: user overrides → inline → stylesheet → cascaded → computed → UA.
    /// Also used by restyle functions that need state-aware lookups.
1661495
    pub(crate) fn get_property_slow<'a>(
1661495
        &'a self,
1661495
        node_data: &'a NodeData,
1661495
        node_id: &NodeId,
1661495
        node_state: &StyledNodeState,
1661495
        css_property_type: &CssPropertyType,
1661495
    ) -> Option<&CssProperty> {
        use azul_css::dynamic_selector::{DynamicSelector, PseudoStateType};
        // First test if there is some user-defined override for the property
1661495
        if let Some(v) = self.user_overridden_properties.get(node_id.index()) {
            if let Ok(idx) = v.binary_search_by_key(css_property_type, |(k, _)| *k) {
                return Some(&v[idx].1);
            }
1661495
        }
        // Helper: do these conditions identify a rule that applies in `state`?
        // Empty conditions = Normal-only. Otherwise all conditions must be
        // PseudoState(state).
1843
        fn matches_pseudo_state(
1843
            conds: &azul_css::dynamic_selector::DynamicSelectorVec,
1843
            state: PseudoStateType,
1843
        ) -> bool {
1843
            let conditions = conds.as_slice();
1843
            if conditions.is_empty() {
950
                state == PseudoStateType::Normal
            } else {
893
                conditions
893
                    .iter()
893
                    .all(|c| matches!(c, DynamicSelector::PseudoState(s) if *s == state))
            }
1843
        }
        // If that fails, see if there is an inline CSS property that matches
        // :focus > :active > :hover > normal (fallback)
1661495
        if node_state.focused {
            // PRIORITY 1: Inline CSS properties (highest priority per CSS spec)
684
            if let Some(p) = node_data.style.iter_inline_properties().find_map(|(prop, conds)| {
684
                if matches_pseudo_state(conds,PseudoStateType::Focus)
342
                    && prop.get_type() == *css_property_type
                {
342
                    Some(prop)
                } else {
342
                    None
                }
684
            }) {
342
                return Some(p);
            }
            // PRIORITY 2: CSS stylesheet properties
            if let Some(p) = Self::find_in_stateful(
                self.css_props.get_slice(node_id.index()),
                PseudoStateType::Focus,
                css_property_type,
            ) {
                return Some(p);
            }
            // PRIORITY 3: Cascaded/inherited properties
            if let Some(p) = Self::find_in_stateful(
                self.cascaded_props.get_slice(node_id.index()),
                PseudoStateType::Focus,
                css_property_type,
            ) {
                return Some(p);
            }
1661153
        }
1661153
        if node_state.active {
            // PRIORITY 1: Inline CSS properties (highest priority per CSS spec)
380
            if let Some(p) = node_data.style.iter_inline_properties().find_map(|(prop, conds)| {
380
                if matches_pseudo_state(conds,PseudoStateType::Active)
95
                    && prop.get_type() == *css_property_type
                {
95
                    Some(prop)
                } else {
285
                    None
                }
380
            }) {
95
                return Some(p);
            }
            // PRIORITY 2: CSS stylesheet properties
            if let Some(p) = Self::find_in_stateful(
                self.css_props.get_slice(node_id.index()),
                PseudoStateType::Active,
                css_property_type,
            ) {
                return Some(p);
            }
            // PRIORITY 3: Cascaded/inherited properties
            if let Some(p) = Self::find_in_stateful(
                self.cascaded_props.get_slice(node_id.index()),
                PseudoStateType::Active,
                css_property_type,
            ) {
                return Some(p);
            }
1661058
        }
        // :dragging pseudo-state (higher priority than :hover)
1661058
        if node_state.dragging {
            if let Some(p) = node_data.style.iter_inline_properties().find_map(|(prop, conds)| {
                if matches_pseudo_state(conds,PseudoStateType::Dragging)
                    && prop.get_type() == *css_property_type
                {
                    Some(prop)
                } else {
                    None
                }
            }) {
                return Some(p);
            }
            if let Some(p) = Self::find_in_stateful(
                self.css_props.get_slice(node_id.index()),
                PseudoStateType::Dragging,
                css_property_type,
            ) {
                return Some(p);
            }
            if let Some(p) = Self::find_in_stateful(
                self.cascaded_props.get_slice(node_id.index()),
                PseudoStateType::Dragging,
                css_property_type,
            ) {
                return Some(p);
            }
1661058
        }
        // :drag-over pseudo-state (higher priority than :hover)
1661058
        if node_state.drag_over {
            if let Some(p) = node_data.style.iter_inline_properties().find_map(|(prop, conds)| {
                if matches_pseudo_state(conds,PseudoStateType::DragOver)
                    && prop.get_type() == *css_property_type
                {
                    Some(prop)
                } else {
                    None
                }
            }) {
                return Some(p);
            }
            if let Some(p) = Self::find_in_stateful(
                self.css_props.get_slice(node_id.index()),
                PseudoStateType::DragOver,
                css_property_type,
            ) {
                return Some(p);
            }
            if let Some(p) = Self::find_in_stateful(
                self.cascaded_props.get_slice(node_id.index()),
                PseudoStateType::DragOver,
                css_property_type,
            ) {
                return Some(p);
            }
1661058
        }
1661058
        if node_state.hover {
            // PRIORITY 1: Inline CSS properties (highest priority per CSS spec)
399
            if let Some(p) = node_data.style.iter_inline_properties().find_map(|(prop, conds)| {
399
                if matches_pseudo_state(conds,PseudoStateType::Hover)
133
                    && prop.get_type() == *css_property_type
                {
133
                    Some(prop)
                } else {
266
                    None
                }
399
            }) {
133
                return Some(p);
            }
            // PRIORITY 2: CSS stylesheet properties
            if let Some(p) = Self::find_in_stateful(
                self.css_props.get_slice(node_id.index()),
                PseudoStateType::Hover,
                css_property_type,
            ) {
                return Some(p);
            }
            // PRIORITY 3: Cascaded/inherited properties
            if let Some(p) = Self::find_in_stateful(
                self.cascaded_props.get_slice(node_id.index()),
                PseudoStateType::Hover,
                css_property_type,
            ) {
                return Some(p);
            }
1660925
        }
        // Normal/fallback properties - always apply as base layer
        // PRIORITY 1: Inline CSS properties (highest priority per CSS spec)
1660925
        if let Some(p) = node_data.style.iter_inline_properties().find_map(|(prop, conds)| {
380
            if matches_pseudo_state(conds, PseudoStateType::Normal)
380
                && prop.get_type() == *css_property_type
            {
380
                Some(prop)
            } else {
                None
            }
380
        }) {
380
            return Some(p);
1660545
        }
        // PRIORITY 2: CSS stylesheet properties
1660545
        if let Some(p) = Self::find_in_stateful(
1660545
            self.css_props.get_slice(node_id.index()),
1660545
            PseudoStateType::Normal,
1660545
            css_property_type,
1660545
        ) {
20590
            return Some(p);
1639955
        }
        // PRIORITY 2b: Global `*` selector properties (specificity 0,0,0)
        // These are collected once during restyle and apply to all nodes.
        // Lower priority than per-node rules but higher than inheritance/UA.
1639955
        if let Some(p) = self.global_css_props.iter().find(|p| p.get_type() == *css_property_type) {
            return Some(p);
1639955
        }
        // PRIORITY 3: Cascaded/inherited properties
1639955
        if let Some(p) = Self::find_in_stateful(
1639955
            self.cascaded_props.get_slice(node_id.index()),
1639955
            PseudoStateType::Normal,
1639955
            css_property_type,
1639955
        ) {
30957
            return Some(p);
1608998
        }
        // Check computed values cache for inherited properties
        // Sorted Vec with binary search
1608998
        if css_property_type.is_inheritable() {
1479051
            if let Some(vec) = self.computed_values.get(node_id.index()) {
1479051
                if let Ok(idx) = vec.binary_search_by_key(css_property_type, |(k, _)| *k) {
                    return Some(&vec[idx].1.property);
1479051
                }
            }
129947
        }
        // User-agent stylesheet fallback (lowest precedence)
        // Check if the node type has a default value for this property
1608998
        crate::ua_css::get_ua_property(&node_data.node_type, *css_property_type)
1661495
    }
    /// Get a CSS property using DynamicSelectorContext for evaluation.
    ///
    /// This is the new API that supports @media queries, @container queries,
    /// OS-specific styles, and all pseudo-states via `CssPropertyWithConditions`.
    ///
    /// The evaluation follows "last wins" semantics - properties are evaluated
    /// in reverse order and the first matching property wins.
    pub(crate) fn get_property_with_context<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        context: &DynamicSelectorContext,
        css_property_type: &CssPropertyType,
    ) -> Option<&CssProperty> {
        // First test if there is some user-defined override for the property
        if let Some(v) = self.user_overridden_properties.get(node_id.index()) {
            if let Ok(idx) = v.binary_search_by_key(css_property_type, |(k, _)| *k) {
                return Some(&v[idx].1);
            }
        }
        // Check inline CSS properties with DynamicSelectorContext evaluation.
        // Iterate in REVERSE order across the flat (prop, conds) view —
        // "last found wins" semantics, replacing the old Focus > Active >
        // Hover > Normal priority chain.
        let inline_props_rev: Vec<_> = node_data
            .style
            .iter_inline_properties()
            .collect::<Vec<_>>();
        if let Some(prop) = inline_props_rev.into_iter().rev().find_map(|(prop, conds)| {
            let conditions_match = conds.as_slice().iter().all(|c| c.matches(context));
            if prop.get_type() == *css_property_type && conditions_match {
                Some(prop)
            } else {
                None
            }
        }) {
            return Some(prop);
        }
        // Fall back to CSS file and cascaded properties
        let legacy_state = StyledNodeState::from_pseudo_state_flags(&context.pseudo_state);
        if let Some(p) = self.get_property(node_data, node_id, &legacy_state, css_property_type) {
            return Some(p);
        }
        None
    }
    /// Check if any properties with conditions would change between two contexts.
    /// This is used for re-layout detection on viewport/container resize.
    pub(crate) fn check_properties_changed(
        node_data: &NodeData,
        old_context: &DynamicSelectorContext,
        new_context: &DynamicSelectorContext,
    ) -> bool {
        for (_prop, conds) in node_data.style.iter_inline_properties() {
            let was_active = conds.as_slice().iter().all(|c| c.matches(old_context));
            let is_active = conds.as_slice().iter().all(|c| c.matches(new_context));
            if was_active != is_active {
                return true;
            }
        }
        false
    }
    /// Check if any layout-affecting properties would change between two contexts.
    /// This is a more targeted check for re-layout detection.
    pub(crate) fn check_layout_properties_changed(
        node_data: &NodeData,
        old_context: &DynamicSelectorContext,
        new_context: &DynamicSelectorContext,
    ) -> bool {
        for (prop, conds) in node_data.style.iter_inline_properties() {
            // Skip non-layout-affecting properties
            if !prop.get_type().can_trigger_relayout() {
                continue;
            }
            let was_active = conds.as_slice().iter().all(|c| c.matches(old_context));
            let is_active = conds.as_slice().iter().all(|c| c.matches(new_context));
            if was_active != is_active {
                return true;
            }
        }
        false
    }
20023
    pub fn get_background_content<'a>(
20023
        &'a self,
20023
        node_data: &'a NodeData,
20023
        node_id: &NodeId,
20023
        node_state: &StyledNodeState,
20023
    ) -> Option<&'a StyleBackgroundContentVecValue> {
20023
        self.get_property(
20023
            node_data,
20023
            node_id,
20023
            node_state,
20023
            &CssPropertyType::BackgroundContent,
        )
20023
        .and_then(|p| p.as_background_content())
20023
    }
    /// Method for getting hyphens property
    pub fn get_hyphens<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleHyphensValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Hyphens)
            .and_then(|p| p.as_hyphens())
    }
    /// Method for getting word-break property
    pub fn get_word_break<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleWordBreakValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::WordBreak)
            .and_then(|p| p.as_word_break())
    }
    /// Method for getting overflow-wrap property
51
    pub fn get_overflow_wrap<'a>(
51
        &'a self,
51
        node_data: &'a NodeData,
51
        node_id: &NodeId,
51
        node_state: &StyledNodeState,
51
    ) -> Option<&'a StyleOverflowWrapValue> {
51
        self.get_property(node_data, node_id, node_state, &CssPropertyType::OverflowWrap)
51
            .and_then(|p| p.as_overflow_wrap())
51
    }
    /// Method for getting line-break property
    pub fn get_line_break<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleLineBreakValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::LineBreak)
            .and_then(|p| p.as_line_break())
    }
    /// Method for getting text-align-last property
    pub fn get_text_align_last<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleTextAlignLastValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::TextAlignLast)
            .and_then(|p| p.as_text_align_last())
    }
    /// Method for getting object-fit property
    pub fn get_object_fit<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleObjectFitValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ObjectFit)
            .and_then(|p| p.as_object_fit())
    }
    /// Method for getting text-orientation property
    pub fn get_text_orientation<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleTextOrientationValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::TextOrientation)
            .and_then(|p| p.as_text_orientation())
    }
    /// Method for getting object-position property
    pub fn get_object_position<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleObjectPositionValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ObjectPosition)
            .and_then(|p| p.as_object_position())
    }
    /// Method for getting aspect-ratio property
408
    pub fn get_aspect_ratio<'a>(
408
        &'a self,
408
        node_data: &'a NodeData,
408
        node_id: &NodeId,
408
        node_state: &StyledNodeState,
408
    ) -> Option<&'a StyleAspectRatioValue> {
408
        self.get_property(node_data, node_id, node_state, &CssPropertyType::AspectRatio)
408
            .and_then(|p| p.as_aspect_ratio())
408
    }
    /// Method for getting direction property
    pub fn get_direction<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleDirectionValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Direction)
            .and_then(|p| p.as_direction())
    }
    pub fn get_unicode_bidi<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleUnicodeBidiValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::UnicodeBidi)
            .and_then(|p| p.as_unicode_bidi())
    }
    pub fn get_text_box_trim<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleTextBoxTrimValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::TextBoxTrim)
            .and_then(|p| p.as_text_box_trim())
    }
    pub fn get_text_box_edge<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleTextBoxEdgeValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::TextBoxEdge)
            .and_then(|p| p.as_text_box_edge())
    }
    pub fn get_dominant_baseline<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleDominantBaselineValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::DominantBaseline)
            .and_then(|p| p.as_dominant_baseline())
    }
    pub fn get_alignment_baseline<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleAlignmentBaselineValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::AlignmentBaseline)
            .and_then(|p| p.as_alignment_baseline())
    }
    pub fn get_initial_letter_align<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleInitialLetterAlignValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::InitialLetterAlign)
            .and_then(|p| p.as_initial_letter_align())
    }
    pub fn get_initial_letter_wrap<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleInitialLetterWrapValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::InitialLetterWrap)
            .and_then(|p| p.as_initial_letter_wrap())
    }
    pub fn get_scrollbar_gutter<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleScrollbarGutterValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ScrollbarGutter)
            .and_then(|p| p.as_scrollbar_gutter())
    }
    pub fn get_overflow_clip_margin<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleOverflowClipMarginValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::OverflowClipMargin)
            .and_then(|p| p.as_overflow_clip_margin())
    }
    pub fn get_clip<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleClipRectValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Clip)
            .and_then(|p| p.as_clip())
    }
    /// Method for getting white-space property
    pub fn get_white_space<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleWhiteSpaceValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::WhiteSpace)
            .and_then(|p| p.as_white_space())
    }
    pub fn get_background_position<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBackgroundPositionVecValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BackgroundPosition,
        )
        .and_then(|p| p.as_background_position())
    }
    pub fn get_background_size<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBackgroundSizeVecValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BackgroundSize,
        )
        .and_then(|p| p.as_background_size())
    }
    pub fn get_background_repeat<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBackgroundRepeatVecValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BackgroundRepeat,
        )
        .and_then(|p| p.as_background_repeat())
    }
1374042
    pub fn get_font_size<'a>(
1374042
        &'a self,
1374042
        node_data: &'a NodeData,
1374042
        node_id: &NodeId,
1374042
        node_state: &StyledNodeState,
1374042
    ) -> Option<&'a StyleFontSizeValue> {
1374042
        self.get_property(node_data, node_id, node_state, &CssPropertyType::FontSize)
1374042
            .and_then(|p| p.as_font_size())
1374042
    }
    pub fn get_font_family<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleFontFamilyVecValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::FontFamily)
            .and_then(|p| p.as_font_family())
    }
    pub fn get_font_weight<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleFontWeightValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::FontWeight)
            .and_then(|p| p.as_font_weight())
    }
    pub fn get_font_style<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleFontStyleValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::FontStyle)
            .and_then(|p| p.as_font_style())
    }
63087
    pub fn get_text_color<'a>(
63087
        &'a self,
63087
        node_data: &'a NodeData,
63087
        node_id: &NodeId,
63087
        node_state: &StyledNodeState,
63087
    ) -> Option<&'a StyleTextColorValue> {
63087
        self.get_property(node_data, node_id, node_state, &CssPropertyType::TextColor)
63087
            .and_then(|p| p.as_text_color())
63087
    }
    /// Method for getting text-indent property
    pub fn get_text_indent<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleTextIndentValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::TextIndent)
            .and_then(|p| p.as_text_indent())
    }
    /// Method for getting initial-letter property
    pub fn get_initial_letter<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleInitialLetterValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::InitialLetter,
        )
        .and_then(|p| p.as_initial_letter())
    }
    /// Method for getting line-clamp property
    pub fn get_line_clamp<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleLineClampValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::LineClamp)
            .and_then(|p| p.as_line_clamp())
    }
    /// Method for getting hanging-punctuation property
    pub fn get_hanging_punctuation<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleHangingPunctuationValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::HangingPunctuation,
        )
        .and_then(|p| p.as_hanging_punctuation())
    }
    /// Method for getting text-combine-upright property
    pub fn get_text_combine_upright<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleTextCombineUprightValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::TextCombineUpright,
        )
        .and_then(|p| p.as_text_combine_upright())
    }
    /// Method for getting -azul-exclusion-margin property
    pub fn get_exclusion_margin<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleExclusionMarginValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ExclusionMargin,
        )
        .and_then(|p| p.as_exclusion_margin())
    }
    /// Method for getting -azul-hyphenation-language property
    pub fn get_hyphenation_language<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleHyphenationLanguageValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::HyphenationLanguage,
        )
        .and_then(|p| p.as_hyphenation_language())
    }
    /// Method for getting caret-color property
1530
    pub fn get_caret_color<'a>(
1530
        &'a self,
1530
        node_data: &'a NodeData,
1530
        node_id: &NodeId,
1530
        node_state: &StyledNodeState,
1530
    ) -> Option<&'a CaretColorValue> {
1530
        self.get_property(node_data, node_id, node_state, &CssPropertyType::CaretColor)
1530
            .and_then(|p| p.as_caret_color())
1530
    }
    /// Method for getting -azul-caret-width property
1530
    pub fn get_caret_width<'a>(
1530
        &'a self,
1530
        node_data: &'a NodeData,
1530
        node_id: &NodeId,
1530
        node_state: &StyledNodeState,
1530
    ) -> Option<&'a CaretWidthValue> {
1530
        self.get_property(node_data, node_id, node_state, &CssPropertyType::CaretWidth)
1530
            .and_then(|p| p.as_caret_width())
1530
    }
    /// Method for getting caret-animation-duration property
1530
    pub fn get_caret_animation_duration<'a>(
1530
        &'a self,
1530
        node_data: &'a NodeData,
1530
        node_id: &NodeId,
1530
        node_state: &StyledNodeState,
1530
    ) -> Option<&'a CaretAnimationDurationValue> {
1530
        self.get_property(
1530
            node_data,
1530
            node_id,
1530
            node_state,
1530
            &CssPropertyType::CaretAnimationDuration,
        )
1530
        .and_then(|p| p.as_caret_animation_duration())
1530
    }
    /// Method for getting selection-background-color property
    pub fn get_selection_background_color<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a SelectionBackgroundColorValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::SelectionBackgroundColor,
        )
        .and_then(|p| p.as_selection_background_color())
    }
    /// Method for getting selection-color property
    pub fn get_selection_color<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a SelectionColorValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::SelectionColor,
        )
        .and_then(|p| p.as_selection_color())
    }
    /// Method for getting -azul-selection-radius property
    pub fn get_selection_radius<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a SelectionRadiusValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::SelectionRadius,
        )
        .and_then(|p| p.as_selection_radius())
    }
    /// Method for getting text-justify property
    pub fn get_text_justify<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutTextJustifyValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::TextJustify,
        )
        .and_then(|p| p.as_text_justify())
    }
    /// Method for getting z-index property
    pub fn get_z_index<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutZIndexValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ZIndex)
            .and_then(|p| p.as_z_index())
    }
    /// Method for getting flex-basis property
    pub fn get_flex_basis<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutFlexBasisValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::FlexBasis)
            .and_then(|p| p.as_flex_basis())
    }
    /// Method for getting column-gap property
    pub fn get_column_gap<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutColumnGapValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ColumnGap)
            .and_then(|p| p.as_column_gap())
    }
    /// Method for getting row-gap property
    pub fn get_row_gap<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutRowGapValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::RowGap)
            .and_then(|p| p.as_row_gap())
    }
    /// Method for getting grid-template-columns property
    pub fn get_grid_template_columns<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutGridTemplateColumnsValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::GridTemplateColumns,
        )
        .and_then(|p| p.as_grid_template_columns())
    }
    /// Method for getting grid-template-rows property
    pub fn get_grid_template_rows<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutGridTemplateRowsValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::GridTemplateRows,
        )
        .and_then(|p| p.as_grid_template_rows())
    }
    /// Method for getting grid-auto-columns property
    pub fn get_grid_auto_columns<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutGridAutoColumnsValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::GridAutoColumns,
        )
        .and_then(|p| p.as_grid_auto_columns())
    }
    /// Method for getting grid-auto-rows property
    pub fn get_grid_auto_rows<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutGridAutoRowsValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::GridAutoRows,
        )
        .and_then(|p| p.as_grid_auto_rows())
    }
    /// Method for getting grid-column property
    pub fn get_grid_column<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutGridColumnValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::GridColumn)
            .and_then(|p| p.as_grid_column())
    }
    /// Method for getting grid-row property
    pub fn get_grid_row<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutGridRowValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::GridRow)
            .and_then(|p| p.as_grid_row())
    }
    /// Method for getting grid-auto-flow property
    pub fn get_grid_auto_flow<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutGridAutoFlowValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::GridAutoFlow,
        )
        .and_then(|p| p.as_grid_auto_flow())
    }
    /// Method for getting justify-self property
    pub fn get_justify_self<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutJustifySelfValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::JustifySelf,
        )
        .and_then(|p| p.as_justify_self())
    }
    /// Method for getting justify-items property
    pub fn get_justify_items<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutJustifyItemsValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::JustifyItems,
        )
        .and_then(|p| p.as_justify_items())
    }
    /// Method for getting gap property
    pub fn get_gap<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutGapValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Gap)
            .and_then(|p| p.as_gap())
    }
    /// Method for getting grid-gap property
    pub(crate) fn get_grid_gap<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutGapValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::GridGap)
            .and_then(|p| p.as_grid_gap())
    }
    /// Method for getting align-self property
    pub fn get_align_self<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutAlignSelfValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::AlignSelf)
            .and_then(|p| p.as_align_self())
    }
    /// Method for getting font property
    pub fn get_font<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleFontValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Font)
            .and_then(|p| p.as_font())
    }
    /// Method for getting writing-mode property
    pub fn get_writing_mode<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutWritingModeValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::WritingMode,
        )
        .and_then(|p| p.as_writing_mode())
    }
    /// Method for getting clear property
    pub fn get_clear<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutClearValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Clear)
            .and_then(|p| p.as_clear())
    }
    /// Method for getting shape-outside property
    pub fn get_shape_outside<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ShapeOutsideValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ShapeOutside,
        )
        .and_then(|p| p.as_shape_outside())
    }
    /// Method for getting shape-inside property
    pub fn get_shape_inside<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ShapeInsideValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ShapeInside,
        )
        .and_then(|p| p.as_shape_inside())
    }
    /// Method for getting clip-path property
    pub fn get_clip_path<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ClipPathValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ClipPath)
            .and_then(|p| p.as_clip_path())
    }
    /// Method for getting scrollbar track background
    pub fn get_scrollbar_track<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBackgroundContentValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ScrollbarTrack)
            .and_then(|p| p.as_scrollbar_track())
    }
    /// Method for getting scrollbar thumb background
    pub fn get_scrollbar_thumb<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBackgroundContentValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ScrollbarThumb)
            .and_then(|p| p.as_scrollbar_thumb())
    }
    /// Method for getting scrollbar button background
    pub fn get_scrollbar_button<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBackgroundContentValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ScrollbarButton)
            .and_then(|p| p.as_scrollbar_button())
    }
    /// Method for getting scrollbar corner background
    pub fn get_scrollbar_corner<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBackgroundContentValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ScrollbarCorner)
            .and_then(|p| p.as_scrollbar_corner())
    }
    /// Method for getting scrollbar resizer background
    pub fn get_scrollbar_resizer<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBackgroundContentValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ScrollbarResizer)
            .and_then(|p| p.as_scrollbar_resizer())
    }
    /// Method for getting scrollbar-width property
    pub fn get_scrollbar_width<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutScrollbarWidthValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ScrollbarWidth,
        )
        .and_then(|p| p.as_scrollbar_width())
    }
    /// Method for getting scrollbar-color property
    pub fn get_scrollbar_color<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleScrollbarColorValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ScrollbarColor,
        )
        .and_then(|p| p.as_scrollbar_color())
    }
    /// Method for getting -azul-scrollbar-visibility property
    pub fn get_scrollbar_visibility<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ScrollbarVisibilityModeValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ScrollbarVisibility,
        )
        .and_then(|p| p.as_scrollbar_visibility())
    }
    /// Method for getting -azul-scrollbar-fade-delay property
    pub fn get_scrollbar_fade_delay<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ScrollbarFadeDelayValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ScrollbarFadeDelay,
        )
        .and_then(|p| p.as_scrollbar_fade_delay())
    }
    /// Method for getting -azul-scrollbar-fade-duration property
    pub fn get_scrollbar_fade_duration<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ScrollbarFadeDurationValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ScrollbarFadeDuration,
        )
        .and_then(|p| p.as_scrollbar_fade_duration())
    }
    /// Method for getting visibility property
    pub fn get_visibility<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleVisibilityValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Visibility)
            .and_then(|p| p.as_visibility())
    }
    /// Method for getting break-before property
    pub fn get_break_before<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a PageBreakValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BreakBefore,
        )
        .and_then(|p| p.as_break_before())
    }
    /// Method for getting break-after property
    pub fn get_break_after<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a PageBreakValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::BreakAfter)
            .and_then(|p| p.as_break_after())
    }
    /// Method for getting break-inside property
    pub fn get_break_inside<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a BreakInsideValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BreakInside,
        )
        .and_then(|p| p.as_break_inside())
    }
    /// Method for getting orphans property
    pub fn get_orphans<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a OrphansValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Orphans)
            .and_then(|p| p.as_orphans())
    }
    /// Method for getting widows property
    pub fn get_widows<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a WidowsValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Widows)
            .and_then(|p| p.as_widows())
    }
    /// Method for getting box-decoration-break property
    pub fn get_box_decoration_break<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a BoxDecorationBreakValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BoxDecorationBreak,
        )
        .and_then(|p| p.as_box_decoration_break())
    }
    /// Method for getting column-count property
    pub fn get_column_count<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ColumnCountValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ColumnCount,
        )
        .and_then(|p| p.as_column_count())
    }
    /// Method for getting column-width property
    pub fn get_column_width<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ColumnWidthValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ColumnWidth,
        )
        .and_then(|p| p.as_column_width())
    }
    /// Method for getting column-span property
    pub fn get_column_span<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ColumnSpanValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ColumnSpan)
            .and_then(|p| p.as_column_span())
    }
    /// Method for getting column-fill property
    pub fn get_column_fill<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ColumnFillValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::ColumnFill)
            .and_then(|p| p.as_column_fill())
    }
    /// Method for getting column-rule-width property
    pub fn get_column_rule_width<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ColumnRuleWidthValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ColumnRuleWidth,
        )
        .and_then(|p| p.as_column_rule_width())
    }
    /// Method for getting column-rule-style property
    pub fn get_column_rule_style<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ColumnRuleStyleValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ColumnRuleStyle,
        )
        .and_then(|p| p.as_column_rule_style())
    }
    /// Method for getting column-rule-color property
    pub fn get_column_rule_color<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ColumnRuleColorValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ColumnRuleColor,
        )
        .and_then(|p| p.as_column_rule_color())
    }
    /// Method for getting flow-into property
    pub fn get_flow_into<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a FlowIntoValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::FlowInto)
            .and_then(|p| p.as_flow_into())
    }
    /// Method for getting flow-from property
    pub fn get_flow_from<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a FlowFromValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::FlowFrom)
            .and_then(|p| p.as_flow_from())
    }
    /// Method for getting shape-margin property
    pub fn get_shape_margin<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ShapeMarginValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ShapeMargin,
        )
        .and_then(|p| p.as_shape_margin())
    }
    /// Method for getting shape-image-threshold property
    pub fn get_shape_image_threshold<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ShapeImageThresholdValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ShapeImageThreshold,
        )
        .and_then(|p| p.as_shape_image_threshold())
    }
    /// Method for getting content property
    pub fn get_content<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a ContentValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Content)
            .and_then(|p| p.as_content())
    }
    /// Method for getting counter-reset property
    pub fn get_counter_reset<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a CounterResetValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::CounterReset,
        )
        .and_then(|p| p.as_counter_reset())
    }
    /// Method for getting counter-increment property
    pub fn get_counter_increment<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a CounterIncrementValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::CounterIncrement,
        )
        .and_then(|p| p.as_counter_increment())
    }
    /// Method for getting string-set property
    pub fn get_string_set<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StringSetValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::StringSet)
            .and_then(|p| p.as_string_set())
    }
    pub fn get_text_align<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleTextAlignValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::TextAlign)
            .and_then(|p| p.as_text_align())
    }
38505
    pub fn get_user_select<'a>(
38505
        &'a self,
38505
        node_data: &'a NodeData,
38505
        node_id: &NodeId,
38505
        node_state: &StyledNodeState,
38505
    ) -> Option<&'a StyleUserSelectValue> {
38505
        self.get_property(node_data, node_id, node_state, &CssPropertyType::UserSelect)
38505
            .and_then(|p| p.as_user_select())
38505
    }
12597
    pub fn get_text_decoration<'a>(
12597
        &'a self,
12597
        node_data: &'a NodeData,
12597
        node_id: &NodeId,
12597
        node_state: &StyledNodeState,
12597
    ) -> Option<&'a StyleTextDecorationValue> {
12597
        self.get_property(
12597
            node_data,
12597
            node_id,
12597
            node_state,
12597
            &CssPropertyType::TextDecoration,
        )
12597
        .and_then(|p| p.as_text_decoration())
12597
    }
58752
    pub fn get_vertical_align<'a>(
58752
        &'a self,
58752
        node_data: &'a NodeData,
58752
        node_id: &NodeId,
58752
        node_state: &StyledNodeState,
58752
    ) -> Option<&'a StyleVerticalAlignValue> {
58752
        self.get_property(
58752
            node_data,
58752
            node_id,
58752
            node_state,
58752
            &CssPropertyType::VerticalAlign,
        )
58752
        .and_then(|p| p.as_vertical_align())
58752
    }
    pub fn get_line_height<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleLineHeightValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::LineHeight)
            .and_then(|p| p.as_line_height())
    }
    pub fn get_letter_spacing<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleLetterSpacingValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::LetterSpacing,
        )
        .and_then(|p| p.as_letter_spacing())
    }
    pub fn get_word_spacing<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleWordSpacingValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::WordSpacing,
        )
        .and_then(|p| p.as_word_spacing())
    }
    pub fn get_tab_size<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleTabSizeValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::TabSize)
            .and_then(|p| p.as_tab_size())
    }
18003
    pub fn get_cursor<'a>(
18003
        &'a self,
18003
        node_data: &'a NodeData,
18003
        node_id: &NodeId,
18003
        node_state: &StyledNodeState,
18003
    ) -> Option<&'a StyleCursorValue> {
18003
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Cursor)
18003
            .and_then(|p| p.as_cursor())
18003
    }
51
    pub fn get_box_shadow_left<'a>(
51
        &'a self,
51
        node_data: &'a NodeData,
51
        node_id: &NodeId,
51
        node_state: &StyledNodeState,
51
    ) -> Option<&'a StyleBoxShadowValue> {
51
        self.get_property(
51
            node_data,
51
            node_id,
51
            node_state,
51
            &CssPropertyType::BoxShadowLeft,
        )
51
        .and_then(|p| p.as_box_shadow_left())
51
    }
51
    pub fn get_box_shadow_right<'a>(
51
        &'a self,
51
        node_data: &'a NodeData,
51
        node_id: &NodeId,
51
        node_state: &StyledNodeState,
51
    ) -> Option<&'a StyleBoxShadowValue> {
51
        self.get_property(
51
            node_data,
51
            node_id,
51
            node_state,
51
            &CssPropertyType::BoxShadowRight,
        )
51
        .and_then(|p| p.as_box_shadow_right())
51
    }
51
    pub fn get_box_shadow_top<'a>(
51
        &'a self,
51
        node_data: &'a NodeData,
51
        node_id: &NodeId,
51
        node_state: &StyledNodeState,
51
    ) -> Option<&'a StyleBoxShadowValue> {
51
        self.get_property(
51
            node_data,
51
            node_id,
51
            node_state,
51
            &CssPropertyType::BoxShadowTop,
        )
51
        .and_then(|p| p.as_box_shadow_top())
51
    }
51
    pub fn get_box_shadow_bottom<'a>(
51
        &'a self,
51
        node_data: &'a NodeData,
51
        node_id: &NodeId,
51
        node_state: &StyledNodeState,
51
    ) -> Option<&'a StyleBoxShadowValue> {
51
        self.get_property(
51
            node_data,
51
            node_id,
51
            node_state,
51
            &CssPropertyType::BoxShadowBottom,
        )
51
        .and_then(|p| p.as_box_shadow_bottom())
51
    }
    pub fn get_border_top_color<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderTopColorValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderTopColor,
        )
        .and_then(|p| p.as_border_top_color())
    }
    pub fn get_border_left_color<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderLeftColorValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderLeftColor,
        )
        .and_then(|p| p.as_border_left_color())
    }
    pub fn get_border_right_color<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderRightColorValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderRightColor,
        )
        .and_then(|p| p.as_border_right_color())
    }
    pub fn get_border_bottom_color<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderBottomColorValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderBottomColor,
        )
        .and_then(|p| p.as_border_bottom_color())
    }
    pub fn get_border_top_style<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderTopStyleValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderTopStyle,
        )
        .and_then(|p| p.as_border_top_style())
    }
    pub fn get_border_left_style<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderLeftStyleValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderLeftStyle,
        )
        .and_then(|p| p.as_border_left_style())
    }
    pub fn get_border_right_style<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderRightStyleValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderRightStyle,
        )
        .and_then(|p| p.as_border_right_style())
    }
    pub fn get_border_bottom_style<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderBottomStyleValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderBottomStyle,
        )
        .and_then(|p| p.as_border_bottom_style())
    }
    pub fn get_border_top_left_radius<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderTopLeftRadiusValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderTopLeftRadius,
        )
        .and_then(|p| p.as_border_top_left_radius())
    }
    pub fn get_border_top_right_radius<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderTopRightRadiusValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderTopRightRadius,
        )
        .and_then(|p| p.as_border_top_right_radius())
    }
    pub fn get_border_bottom_left_radius<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderBottomLeftRadiusValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderBottomLeftRadius,
        )
        .and_then(|p| p.as_border_bottom_left_radius())
    }
    pub fn get_border_bottom_right_radius<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderBottomRightRadiusValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderBottomRightRadius,
        )
        .and_then(|p| p.as_border_bottom_right_radius())
    }
    pub fn get_opacity<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleOpacityValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Opacity)
            .and_then(|p| p.as_opacity())
    }
153
    pub fn get_transform<'a>(
153
        &'a self,
153
        node_data: &'a NodeData,
153
        node_id: &NodeId,
153
        node_state: &StyledNodeState,
153
    ) -> Option<&'a StyleTransformVecValue> {
153
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Transform)
153
            .and_then(|p| p.as_transform())
153
    }
153
    pub fn get_transform_origin<'a>(
153
        &'a self,
153
        node_data: &'a NodeData,
153
        node_id: &NodeId,
153
        node_state: &StyledNodeState,
153
    ) -> Option<&'a StyleTransformOriginValue> {
153
        self.get_property(
153
            node_data,
153
            node_id,
153
            node_state,
153
            &CssPropertyType::TransformOrigin,
        )
153
        .and_then(|p| p.as_transform_origin())
153
    }
    pub fn get_perspective_origin<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StylePerspectiveOriginValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::PerspectiveOrigin,
        )
        .and_then(|p| p.as_perspective_origin())
    }
    pub fn get_backface_visibility<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBackfaceVisibilityValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BackfaceVisibility,
        )
        .and_then(|p| p.as_backface_visibility())
    }
57
    pub fn get_display<'a>(
57
        &'a self,
57
        node_data: &'a NodeData,
57
        node_id: &NodeId,
57
        node_state: &StyledNodeState,
57
    ) -> Option<&'a LayoutDisplayValue> {
57
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Display)
57
            .and_then(|p| p.as_display())
57
    }
    pub fn get_float<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutFloatValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Float)
            .and_then(|p| p.as_float())
    }
    pub fn get_box_sizing<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutBoxSizingValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::BoxSizing)
            .and_then(|p| p.as_box_sizing())
    }
57
    pub fn get_width<'a>(
57
        &'a self,
57
        node_data: &'a NodeData,
57
        node_id: &NodeId,
57
        node_state: &StyledNodeState,
57
    ) -> Option<&'a LayoutWidthValue> {
57
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Width)
57
            .and_then(|p| p.as_width())
57
    }
15204
    pub fn get_height<'a>(
15204
        &'a self,
15204
        node_data: &'a NodeData,
15204
        node_id: &NodeId,
15204
        node_state: &StyledNodeState,
15204
    ) -> Option<&'a LayoutHeightValue> {
15204
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Height)
15204
            .and_then(|p| p.as_height())
15204
    }
19
    pub fn get_min_width<'a>(
19
        &'a self,
19
        node_data: &'a NodeData,
19
        node_id: &NodeId,
19
        node_state: &StyledNodeState,
19
    ) -> Option<&'a LayoutMinWidthValue> {
19
        self.get_property(node_data, node_id, node_state, &CssPropertyType::MinWidth)
19
            .and_then(|p| p.as_min_width())
19
    }
    pub fn get_min_height<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutMinHeightValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::MinHeight)
            .and_then(|p| p.as_min_height())
    }
    pub fn get_max_width<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutMaxWidthValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::MaxWidth)
            .and_then(|p| p.as_max_width())
    }
    pub fn get_max_height<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutMaxHeightValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::MaxHeight)
            .and_then(|p| p.as_max_height())
    }
    pub fn get_position<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutPositionValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Position)
            .and_then(|p| p.as_position())
    }
    pub fn get_top<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutTopValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Top)
            .and_then(|p| p.as_top())
    }
    pub fn get_bottom<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutInsetBottomValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Bottom)
            .and_then(|p| p.as_bottom())
    }
    pub fn get_right<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutRightValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Right)
            .and_then(|p| p.as_right())
    }
    pub fn get_left<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutLeftValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Left)
            .and_then(|p| p.as_left())
    }
    pub fn get_padding_top<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutPaddingTopValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::PaddingTop)
            .and_then(|p| p.as_padding_top())
    }
    pub fn get_padding_bottom<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutPaddingBottomValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::PaddingBottom,
        )
        .and_then(|p| p.as_padding_bottom())
    }
    pub fn get_padding_left<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutPaddingLeftValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::PaddingLeft,
        )
        .and_then(|p| p.as_padding_left())
    }
    pub fn get_padding_right<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutPaddingRightValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::PaddingRight,
        )
        .and_then(|p| p.as_padding_right())
    }
223
    pub fn get_margin_top<'a>(
223
        &'a self,
223
        node_data: &'a NodeData,
223
        node_id: &NodeId,
223
        node_state: &StyledNodeState,
223
    ) -> Option<&'a LayoutMarginTopValue> {
223
        self.get_property(node_data, node_id, node_state, &CssPropertyType::MarginTop)
223
            .and_then(|p| p.as_margin_top())
223
    }
255
    pub fn get_margin_bottom<'a>(
255
        &'a self,
255
        node_data: &'a NodeData,
255
        node_id: &NodeId,
255
        node_state: &StyledNodeState,
255
    ) -> Option<&'a LayoutMarginBottomValue> {
255
        self.get_property(
255
            node_data,
255
            node_id,
255
            node_state,
255
            &CssPropertyType::MarginBottom,
        )
255
        .and_then(|p| p.as_margin_bottom())
255
    }
    pub fn get_margin_left<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutMarginLeftValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::MarginLeft)
            .and_then(|p| p.as_margin_left())
    }
    pub fn get_margin_right<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutMarginRightValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::MarginRight,
        )
        .and_then(|p| p.as_margin_right())
    }
    pub fn get_border_top_width<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutBorderTopWidthValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderTopWidth,
        )
        .and_then(|p| p.as_border_top_width())
    }
    pub fn get_border_left_width<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutBorderLeftWidthValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderLeftWidth,
        )
        .and_then(|p| p.as_border_left_width())
    }
    pub fn get_border_right_width<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutBorderRightWidthValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderRightWidth,
        )
        .and_then(|p| p.as_border_right_width())
    }
    pub fn get_border_bottom_width<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutBorderBottomWidthValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderBottomWidth,
        )
        .and_then(|p| p.as_border_bottom_width())
    }
    pub fn get_overflow_x<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutOverflowValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::OverflowX)
            .and_then(|p| p.as_overflow_x())
    }
    pub fn get_overflow_y<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutOverflowValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::OverflowY)
            .and_then(|p| p.as_overflow_y())
    }
    pub fn get_overflow_block<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutOverflowValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::OverflowBlock)
            .and_then(|p| p.as_overflow_block())
    }
    pub fn get_overflow_inline<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutOverflowValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::OverflowInline)
            .and_then(|p| p.as_overflow_inline())
    }
    pub fn get_flex_direction<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutFlexDirectionValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::FlexDirection,
        )
        .and_then(|p| p.as_flex_direction())
    }
3162
    pub fn get_flex_wrap<'a>(
3162
        &'a self,
3162
        node_data: &'a NodeData,
3162
        node_id: &NodeId,
3162
        node_state: &StyledNodeState,
3162
    ) -> Option<&'a LayoutFlexWrapValue> {
3162
        self.get_property(node_data, node_id, node_state, &CssPropertyType::FlexWrap)
3162
            .and_then(|p| p.as_flex_wrap())
3162
    }
    pub fn get_flex_grow<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutFlexGrowValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::FlexGrow)
            .and_then(|p| p.as_flex_grow())
    }
    pub fn get_flex_shrink<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutFlexShrinkValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::FlexShrink)
            .and_then(|p| p.as_flex_shrink())
    }
    pub fn get_justify_content<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutJustifyContentValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::JustifyContent,
        )
        .and_then(|p| p.as_justify_content())
    }
    pub fn get_align_items<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutAlignItemsValue> {
        self.get_property(node_data, node_id, node_state, &CssPropertyType::AlignItems)
            .and_then(|p| p.as_align_items())
    }
    pub fn get_align_content<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutAlignContentValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::AlignContent,
        )
        .and_then(|p| p.as_align_content())
    }
    pub fn get_mix_blend_mode<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleMixBlendModeValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::MixBlendMode,
        )
        .and_then(|p| p.as_mix_blend_mode())
    }
9129
    pub fn get_filter<'a>(
9129
        &'a self,
9129
        node_data: &'a NodeData,
9129
        node_id: &NodeId,
9129
        node_state: &StyledNodeState,
9129
    ) -> Option<&'a StyleFilterVecValue> {
9129
        self.get_property(node_data, node_id, node_state, &CssPropertyType::Filter)
9129
            .and_then(|p| p.as_filter())
9129
    }
9129
    pub fn get_backdrop_filter<'a>(
9129
        &'a self,
9129
        node_data: &'a NodeData,
9129
        node_id: &NodeId,
9129
        node_state: &StyledNodeState,
9129
    ) -> Option<&'a StyleFilterVecValue> {
9129
        self.get_property(node_data, node_id, node_state, &CssPropertyType::BackdropFilter)
9129
            .and_then(|p| p.as_backdrop_filter())
9129
    }
27642
    pub fn get_text_shadow<'a>(
27642
        &'a self,
27642
        node_data: &'a NodeData,
27642
        node_id: &NodeId,
27642
        node_state: &StyledNodeState,
27642
    ) -> Option<&'a StyleBoxShadowValue> {
27642
        self.get_property(node_data, node_id, node_state, &CssPropertyType::TextShadow)
27642
            .and_then(|p| p.as_text_shadow())
27642
    }
    pub fn get_list_style_type<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleListStyleTypeValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ListStyleType,
        )
        .and_then(|p| p.as_list_style_type())
    }
    pub fn get_list_style_position<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleListStylePositionValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::ListStylePosition,
        )
        .and_then(|p| p.as_list_style_position())
    }
1683
    pub fn get_table_layout<'a>(
1683
        &'a self,
1683
        node_data: &'a NodeData,
1683
        node_id: &NodeId,
1683
        node_state: &StyledNodeState,
1683
    ) -> Option<&'a LayoutTableLayoutValue> {
1683
        self.get_property(
1683
            node_data,
1683
            node_id,
1683
            node_state,
1683
            &CssPropertyType::TableLayout,
        )
1683
        .and_then(|p| p.as_table_layout())
1683
    }
    pub fn get_border_collapse<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a StyleBorderCollapseValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderCollapse,
        )
        .and_then(|p| p.as_border_collapse())
    }
    pub fn get_border_spacing<'a>(
        &'a self,
        node_data: &'a NodeData,
        node_id: &NodeId,
        node_state: &StyledNodeState,
    ) -> Option<&'a LayoutBorderSpacingValue> {
        self.get_property(
            node_data,
            node_id,
            node_state,
            &CssPropertyType::BorderSpacing,
        )
        .and_then(|p| p.as_border_spacing())
    }
1683
    pub fn get_caption_side<'a>(
1683
        &'a self,
1683
        node_data: &'a NodeData,
1683
        node_id: &NodeId,
1683
        node_state: &StyledNodeState,
1683
    ) -> Option<&'a StyleCaptionSideValue> {
1683
        self.get_property(
1683
            node_data,
1683
            node_id,
1683
            node_state,
1683
            &CssPropertyType::CaptionSide,
        )
1683
        .and_then(|p| p.as_caption_side())
1683
    }
1734
    pub fn get_empty_cells<'a>(
1734
        &'a self,
1734
        node_data: &'a NodeData,
1734
        node_id: &NodeId,
1734
        node_state: &StyledNodeState,
1734
    ) -> Option<&'a StyleEmptyCellsValue> {
1734
        self.get_property(node_data, node_id, node_state, &CssPropertyType::EmptyCells)
1734
            .and_then(|p| p.as_empty_cells())
1734
    }
    // Width calculation methods
    pub fn calc_width(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_width: f32,
    ) -> f32 {
        self.get_width(node_data, node_id, styled_node_state)
            .and_then(|w| match w.get_property()? {
                LayoutWidth::Px(px) => Some(px.to_pixels_internal(
                    reference_width,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                )),
                _ => Some(0.0), // min-content/max-content not resolved here
            })
            .unwrap_or(0.0)
    }
    pub fn calc_min_width(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_width: f32,
    ) -> f32 {
        self.get_min_width(node_data, node_id, styled_node_state)
            .and_then(|w| {
                Some(w.get_property()?.inner.to_pixels_internal(
                    reference_width,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    pub fn calc_max_width(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_width: f32,
    ) -> Option<f32> {
        self.get_max_width(node_data, node_id, styled_node_state)
            .and_then(|w| {
                Some(w.get_property()?.inner.to_pixels_internal(
                    reference_width,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
    }
    // Height calculation methods
    pub fn calc_height(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_height: f32,
    ) -> f32 {
        self.get_height(node_data, node_id, styled_node_state)
            .and_then(|h| match h.get_property()? {
                LayoutHeight::Px(px) => Some(px.to_pixels_internal(
                    reference_height,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                )),
                _ => Some(0.0), // min-content/max-content not resolved here
            })
            .unwrap_or(0.0)
    }
    pub fn calc_min_height(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_height: f32,
    ) -> f32 {
        self.get_min_height(node_data, node_id, styled_node_state)
            .and_then(|h| {
                Some(h.get_property()?.inner.to_pixels_internal(
                    reference_height,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    pub fn calc_max_height(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_height: f32,
    ) -> Option<f32> {
        self.get_max_height(node_data, node_id, styled_node_state)
            .and_then(|h| {
                Some(h.get_property()?.inner.to_pixels_internal(
                    reference_height,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
    }
    // Position calculation methods
    pub fn calc_left(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_width: f32,
    ) -> Option<f32> {
        self.get_left(node_data, node_id, styled_node_state)
            .and_then(|l| {
                Some(l.get_property()?.inner.to_pixels_internal(
                    reference_width,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
    }
    pub fn calc_right(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_width: f32,
    ) -> Option<f32> {
        self.get_right(node_data, node_id, styled_node_state)
            .and_then(|r| {
                Some(r.get_property()?.inner.to_pixels_internal(
                    reference_width,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
    }
    pub fn calc_top(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_height: f32,
    ) -> Option<f32> {
        self.get_top(node_data, node_id, styled_node_state)
            .and_then(|t| {
                Some(t.get_property()?.inner.to_pixels_internal(
                    reference_height,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
    }
    pub fn calc_bottom(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_height: f32,
    ) -> Option<f32> {
        self.get_bottom(node_data, node_id, styled_node_state)
            .and_then(|b| {
                Some(b.get_property()?.inner.to_pixels_internal(
                    reference_height,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
    }
    // Border calculation methods
    pub fn calc_border_left_width(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_width: f32,
    ) -> f32 {
        self.get_border_left_width(node_data, node_id, styled_node_state)
            .and_then(|b| {
                Some(b.get_property()?.inner.to_pixels_internal(
                    reference_width,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    pub fn calc_border_right_width(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_width: f32,
    ) -> f32 {
        self.get_border_right_width(node_data, node_id, styled_node_state)
            .and_then(|b| {
                Some(b.get_property()?.inner.to_pixels_internal(
                    reference_width,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    pub fn calc_border_top_width(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_height: f32,
    ) -> f32 {
        self.get_border_top_width(node_data, node_id, styled_node_state)
            .and_then(|b| {
                Some(b.get_property()?.inner.to_pixels_internal(
                    reference_height,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    pub fn calc_border_bottom_width(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_height: f32,
    ) -> f32 {
        self.get_border_bottom_width(node_data, node_id, styled_node_state)
            .and_then(|b| {
                Some(b.get_property()?.inner.to_pixels_internal(
                    reference_height,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    // Padding calculation methods
    pub fn calc_padding_left(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_width: f32,
    ) -> f32 {
        self.get_padding_left(node_data, node_id, styled_node_state)
            .and_then(|p| {
                Some(p.get_property()?.inner.to_pixels_internal(
                    reference_width,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    pub fn calc_padding_right(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_width: f32,
    ) -> f32 {
        self.get_padding_right(node_data, node_id, styled_node_state)
            .and_then(|p| {
                Some(p.get_property()?.inner.to_pixels_internal(
                    reference_width,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    pub fn calc_padding_top(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_height: f32,
    ) -> f32 {
        self.get_padding_top(node_data, node_id, styled_node_state)
            .and_then(|p| {
                Some(p.get_property()?.inner.to_pixels_internal(
                    reference_height,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    pub fn calc_padding_bottom(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_height: f32,
    ) -> f32 {
        self.get_padding_bottom(node_data, node_id, styled_node_state)
            .and_then(|p| {
                Some(p.get_property()?.inner.to_pixels_internal(
                    reference_height,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    // Margin calculation methods
    pub fn calc_margin_left(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_width: f32,
    ) -> f32 {
        self.get_margin_left(node_data, node_id, styled_node_state)
            .and_then(|m| {
                Some(m.get_property()?.inner.to_pixels_internal(
                    reference_width,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    pub fn calc_margin_right(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_width: f32,
    ) -> f32 {
        self.get_margin_right(node_data, node_id, styled_node_state)
            .and_then(|m| {
                Some(m.get_property()?.inner.to_pixels_internal(
                    reference_width,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    pub fn calc_margin_top(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_height: f32,
    ) -> f32 {
        self.get_margin_top(node_data, node_id, styled_node_state)
            .and_then(|m| {
                Some(m.get_property()?.inner.to_pixels_internal(
                    reference_height,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
    pub fn calc_margin_bottom(
        &self,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        reference_height: f32,
    ) -> f32 {
        self.get_margin_bottom(node_data, node_id, styled_node_state)
            .and_then(|m| {
                Some(m.get_property()?.inner.to_pixels_internal(
                    reference_height,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                ))
            })
            .unwrap_or(0.0)
    }
134787
    fn resolve_property_dependency(
134787
        target_property: &CssProperty,
134787
        reference_property: &CssProperty,
134787
    ) -> Option<CssProperty> {
        use azul_css::{
            css::CssPropertyValue,
            props::{
                basic::{font::StyleFontSize, length::SizeMetric, pixel::PixelValue},
                layout::*,
                style::{SelectionRadius, StyleLetterSpacing, StyleWordSpacing},
            },
        };
        // Extract PixelValue from various property types (returns owned value)
174843
        let get_pixel_value = |prop: &CssProperty| -> Option<PixelValue> {
174843
            match prop {
55579
                CssProperty::FontSize(val) => val.get_property().map(|v| v.inner),
                CssProperty::LetterSpacing(val) => val.get_property().map(|v| v.inner),
                CssProperty::WordSpacing(val) => val.get_property().map(|v| v.inner),
5189
                CssProperty::PaddingLeft(val) => val.get_property().map(|v| v.inner),
4998
                CssProperty::PaddingRight(val) => val.get_property().map(|v| v.inner),
3927
                CssProperty::PaddingTop(val) => val.get_property().map(|v| v.inner),
3927
                CssProperty::PaddingBottom(val) => val.get_property().map(|v| v.inner),
3162
                CssProperty::MarginLeft(val) => val.get_property().map(|v| v.inner),
408
                CssProperty::MarginRight(val) => val.get_property().map(|v| v.inner),
1257
                CssProperty::MarginTop(val) => val.get_property().map(|v| v.inner),
1257
                CssProperty::MarginBottom(val) => val.get_property().map(|v| v.inner),
                CssProperty::MinWidth(val) => val.get_property().map(|v| v.inner),
408
                CssProperty::MinHeight(val) => val.get_property().map(|v| v.inner),
                CssProperty::MaxWidth(val) => val.get_property().map(|v| v.inner),
                CssProperty::MaxHeight(val) => val.get_property().map(|v| v.inner),
                CssProperty::SelectionRadius(val) => val.get_property().map(|v| v.inner),
94731
                _ => None,
            }
174843
        };
134787
        let target_pixel_value = get_pixel_value(target_property)?;
40056
        let reference_pixel_value = get_pixel_value(reference_property)?;
        // Convert reference to absolute pixels first
40056
        let reference_px = match reference_pixel_value.metric {
40056
            SizeMetric::Px => reference_pixel_value.number.get(),
            SizeMetric::Pt => reference_pixel_value.number.get() * PT_TO_PX,
            SizeMetric::In => reference_pixel_value.number.get() * IN_TO_PX,
            SizeMetric::Cm => reference_pixel_value.number.get() * CM_TO_PX,
            SizeMetric::Mm => reference_pixel_value.number.get() * MM_TO_PX,
            SizeMetric::Em => return None, // Reference can't be relative
            SizeMetric::Rem => return None, // Reference can't be relative
            SizeMetric::Percent => return None, // Reference can't be relative
            // Reference can't be viewport-relative
            SizeMetric::Vw | SizeMetric::Vh | SizeMetric::Vmin | SizeMetric::Vmax => return None,
        };
        // Resolve target based on reference
40056
        let resolved_px = match target_pixel_value.metric {
38010
            SizeMetric::Px => target_pixel_value.number.get(),
            SizeMetric::Pt => target_pixel_value.number.get() * PT_TO_PX,
            SizeMetric::In => target_pixel_value.number.get() * IN_TO_PX,
            SizeMetric::Cm => target_pixel_value.number.get() * CM_TO_PX,
            SizeMetric::Mm => target_pixel_value.number.get() * MM_TO_PX,
1932
            SizeMetric::Em => target_pixel_value.number.get() * reference_px,
            // Use reference as root font-size
            SizeMetric::Rem => target_pixel_value.number.get() * reference_px,
114
            SizeMetric::Percent => target_pixel_value.number.get() / 100.0 * reference_px,
            // Need viewport context
            SizeMetric::Vw | SizeMetric::Vh | SizeMetric::Vmin | SizeMetric::Vmax => return None,
        };
        // Create a new property with the resolved value
40056
        let resolved_pixel_value = PixelValue::px(resolved_px);
40056
        match target_property {
15523
            CssProperty::FontSize(_) => Some(CssProperty::FontSize(CssPropertyValue::Exact(
15523
                StyleFontSize {
15523
                    inner: resolved_pixel_value,
15523
                },
15523
            ))),
            CssProperty::LetterSpacing(_) => Some(CssProperty::LetterSpacing(
                CssPropertyValue::Exact(StyleLetterSpacing {
                    inner: resolved_pixel_value,
                }),
            )),
            CssProperty::WordSpacing(_) => Some(CssProperty::WordSpacing(CssPropertyValue::Exact(
                StyleWordSpacing {
                    inner: resolved_pixel_value,
                },
            ))),
5189
            CssProperty::PaddingLeft(_) => Some(CssProperty::PaddingLeft(CssPropertyValue::Exact(
5189
                LayoutPaddingLeft {
5189
                    inner: resolved_pixel_value,
5189
                },
5189
            ))),
4998
            CssProperty::PaddingRight(_) => Some(CssProperty::PaddingRight(
4998
                CssPropertyValue::Exact(LayoutPaddingRight {
4998
                    inner: resolved_pixel_value,
4998
                }),
4998
            )),
3927
            CssProperty::PaddingTop(_) => Some(CssProperty::PaddingTop(CssPropertyValue::Exact(
3927
                LayoutPaddingTop {
3927
                    inner: resolved_pixel_value,
3927
                },
3927
            ))),
3927
            CssProperty::PaddingBottom(_) => Some(CssProperty::PaddingBottom(
3927
                CssPropertyValue::Exact(LayoutPaddingBottom {
3927
                    inner: resolved_pixel_value,
3927
                }),
3927
            )),
3162
            CssProperty::MarginLeft(_) => Some(CssProperty::MarginLeft(CssPropertyValue::Exact(
3162
                LayoutMarginLeft {
3162
                    inner: resolved_pixel_value,
3162
                },
3162
            ))),
408
            CssProperty::MarginRight(_) => Some(CssProperty::MarginRight(CssPropertyValue::Exact(
408
                LayoutMarginRight {
408
                    inner: resolved_pixel_value,
408
                },
408
            ))),
1257
            CssProperty::MarginTop(_) => Some(CssProperty::MarginTop(CssPropertyValue::Exact(
1257
                LayoutMarginTop {
1257
                    inner: resolved_pixel_value,
1257
                },
1257
            ))),
1257
            CssProperty::MarginBottom(_) => Some(CssProperty::MarginBottom(
1257
                CssPropertyValue::Exact(LayoutMarginBottom {
1257
                    inner: resolved_pixel_value,
1257
                }),
1257
            )),
            CssProperty::MinWidth(_) => Some(CssProperty::MinWidth(CssPropertyValue::Exact(
                LayoutMinWidth {
                    inner: resolved_pixel_value,
                },
            ))),
408
            CssProperty::MinHeight(_) => Some(CssProperty::MinHeight(CssPropertyValue::Exact(
408
                LayoutMinHeight {
408
                    inner: resolved_pixel_value,
408
                },
408
            ))),
            CssProperty::MaxWidth(_) => Some(CssProperty::MaxWidth(CssPropertyValue::Exact(
                LayoutMaxWidth {
                    inner: resolved_pixel_value,
                },
            ))),
            CssProperty::MaxHeight(_) => Some(CssProperty::MaxHeight(CssPropertyValue::Exact(
                LayoutMaxHeight {
                    inner: resolved_pixel_value,
                },
            ))),
            CssProperty::SelectionRadius(_) => Some(CssProperty::SelectionRadius(
                CssPropertyValue::Exact(SelectionRadius {
                    inner: resolved_pixel_value,
                }),
            )),
            _ => None,
        }
134787
    }
    /// Applies user-agent (UA) CSS properties to the cascade before inheritance.
    ///
    /// UA CSS has the lowest priority in the cascade, so it should only be applied
    /// if the node doesn't already have the property from inline styles or author CSS.
    ///
    /// This is critical for text nodes: UA CSS properties (like font-weight: bold for H1)
    /// must be in the cascade maps so they can be inherited by child text nodes.
    ///
    /// Uses a bitset per node to avoid O(n²) scanning of property vecs.
12037
    pub fn apply_ua_css(&mut self, node_data: &[NodeData]) {
        use azul_css::props::property::CssPropertyType;
        use azul_css::dynamic_selector::PseudoStateType;
12037
        let node_count = node_data.len();
12037
        if node_count == 0 {
            return;
12037
        }
        // Build a bitset per node: which CssPropertyType values are already set (Normal state).
        // CssPropertyType has ~178 variants, so we need [u128; 2] per node (256 bits).
12037
        let mut prop_set: Vec<[u128; 2]> = vec![[0u128; 2]; node_count];
        // Mark properties from css_props (author CSS, Normal state)
85184
        for (node_idx, props) in self.css_props.iter_node_slices() {
300719
            for p in props.iter() {
296729
                if p.state == PseudoStateType::Normal {
296729
                    let d = p.prop_type as u16 as usize;
296729
                    if d < 128 {
296404
                        prop_set[node_idx][0] |= 1u128 << d;
296404
                    } else {
325
                        prop_set[node_idx][1] |= 1u128 << (d - 128);
325
                    }
                }
            }
        }
        // Mark properties from cascaded_props (Normal state)
85184
        for (node_idx, props) in self.cascaded_props.iter_node_slices() {
85184
            for p in props.iter() {
34704
                if p.state == PseudoStateType::Normal {
34704
                    let d = p.prop_type as u16 as usize;
34704
                    if d < 128 {
34704
                        prop_set[node_idx][0] |= 1u128 << d;
34704
                    } else {
                        prop_set[node_idx][1] |= 1u128 << (d - 128);
                    }
                }
            }
        }
        // Mark properties from inline CSS (NodeData.style, unconditional = Normal)
85184
        for (node_idx, node) in node_data.iter().enumerate() {
85184
            for (prop, conds) in node.style.iter_inline_properties() {
3686
                let is_normal = conds.as_slice().is_empty();
3686
                if is_normal {
1425
                    let d = prop.get_type() as u16 as usize;
1425
                    if d < 128 {
1406
                        prop_set[node_idx][0] |= 1u128 << d;
1406
                    } else {
19
                        prop_set[node_idx][1] |= 1u128 << (d - 128);
19
                    }
2261
                }
            }
        }
        // All UA property types that get_ua_property() may return Some for
12037
        let property_types = [
12037
            CssPropertyType::Display,
12037
            CssPropertyType::Width,
12037
            CssPropertyType::Height,
12037
            CssPropertyType::FontSize,
12037
            CssPropertyType::FontWeight,
12037
            CssPropertyType::FontFamily,
12037
            CssPropertyType::MarginTop,
12037
            CssPropertyType::MarginBottom,
12037
            CssPropertyType::MarginLeft,
12037
            CssPropertyType::MarginRight,
12037
            CssPropertyType::PaddingTop,
12037
            CssPropertyType::PaddingBottom,
12037
            CssPropertyType::PaddingLeft,
12037
            CssPropertyType::PaddingRight,
12037
            CssPropertyType::BorderTopStyle,
12037
            CssPropertyType::BorderTopWidth,
12037
            CssPropertyType::BorderTopColor,
12037
            CssPropertyType::BreakInside,
12037
            CssPropertyType::BreakAfter,
12037
            CssPropertyType::ListStyleType,
12037
            CssPropertyType::CounterReset,
12037
            CssPropertyType::TextDecoration,
12037
            CssPropertyType::TextAlign,
12037
            CssPropertyType::VerticalAlign,
12037
            CssPropertyType::Cursor,
12037
        ];
        // Apply UA CSS: only insert for property types not yet set (bitset check = O(1))
85184
        for (node_index, node) in node_data.iter().enumerate() {
85184
            let node_type = &node.node_type;
2214784
            for prop_type in &property_types {
                // Check bitset: if already set, skip entirely
2129600
                let d = *prop_type as u16 as usize;
2129600
                let has_prop = if d < 128 {
1788864
                    (prop_set[node_index][0] & (1u128 << d)) != 0
                } else {
340736
                    (prop_set[node_index][1] & (1u128 << (d - 128))) != 0
                };
2129600
                if has_prop {
153115
                    continue;
1976485
                }
                // Check if UA CSS defines this property for this node type
1976485
                if let Some(ua_prop) = crate::ua_css::get_ua_property(node_type, *prop_type) {
160372
                    self.cascaded_props.push_to(node_index, StatefulCssProperty {
160372
                        state: PseudoStateType::Normal,
160372
                        prop_type: *prop_type,
160372
                        property: ua_prop.clone(),
160372
                    });
                    // Mark as set in the bitset (prevent duplicate insertion for same node)
160372
                    if d < 128 {
158397
                        prop_set[node_index][0] |= 1u128 << d;
158397
                    } else {
1975
                        prop_set[node_index][1] |= 1u128 << (d - 128);
1975
                    }
1816113
                }
            }
        }
12037
    }
    /// Sort cascaded_props by (state, prop_type) and flatten into contiguous memory.
    /// Must be called after apply_ua_css() which adds entries to cascaded_props.
    pub fn sort_cascaded_props(&mut self) {
        self.cascaded_props.sort_each_and_flatten(|p| (p.state, p.prop_type));
    }
    /// Compute inherited values for all nodes in the DOM tree.
    ///
    /// Implements CSS inheritance: walk tree depth-first, apply cascade priority
    /// (inherited → cascaded → css → inline → user), create dependency chains for
    /// relative values. Call `apply_ua_css()` before this function.
12759
    pub fn compute_inherited_values(
12759
        &mut self,
12759
        node_hierarchy: &[NodeHierarchyItem],
12759
        node_data: &[NodeData],
12759
    ) -> Vec<NodeId> {
12759
        if self.computed_values.len() < node_hierarchy.len() {
12037
            self.computed_values.resize(node_hierarchy.len(), Vec::new());
12037
        }
12759
        node_hierarchy
12759
            .iter()
12759
            .enumerate()
87578
            .filter_map(|(node_index, hierarchy_item)| {
87578
                let node_id = NodeId::new(node_index);
87578
                let parent_id = hierarchy_item.parent_id();
87578
                let parent_computed: Option<Vec<(CssPropertyType, CssPropertyWithOrigin)>> =
87578
                    parent_id.and_then(|pid| self.computed_values.get(pid.index()).cloned());
87578
                let mut ctx = InheritanceContext {
87578
                    node_id,
87578
                    parent_id,
87578
                    computed_values: Vec::new(),
87578
                };
                // Step 1: Inherit from parent
87578
                if let Some(ref parent_values) = parent_computed {
74819
                    self.inherit_from_parent(&mut ctx, parent_values);
74819
                }
                // Steps 2-5: Apply cascade in priority order
87578
                self.apply_cascade_properties(
87578
                    &mut ctx,
87578
                    node_id,
87578
                    &parent_computed,
87578
                    node_data,
87578
                    node_index,
                );
                // Check for changes and store
87578
                let changed = self.store_if_changed(&ctx);
87578
                changed.then_some(node_id)
87578
            })
12759
            .collect()
12759
    }
    /// Inherit inheritable properties from parent node
74819
    fn inherit_from_parent(
74819
        &self,
74819
        ctx: &mut InheritanceContext,
74819
        parent_values: &[(CssPropertyType, CssPropertyWithOrigin)],
74819
    ) {
39053
        for (prop_type, prop_with_origin) in
480411
            parent_values.iter().filter(|(pt, _)| pt.is_inheritable())
        {
39053
            let entry = (*prop_type, CssPropertyWithOrigin {
39053
                property: prop_with_origin.property.clone(),
39053
                origin: CssPropertyOrigin::Inherited,
39053
            });
            // Insert into sorted vec
39053
            match ctx.computed_values.binary_search_by_key(prop_type, |(k, _)| *k) {
                Ok(idx) => ctx.computed_values[idx] = entry,
39053
                Err(idx) => ctx.computed_values.insert(idx, entry),
            }
        }
74819
    }
    /// Apply all cascade properties in priority order
87578
    fn apply_cascade_properties(
87578
        &self,
87578
        ctx: &mut InheritanceContext,
87578
        node_id: NodeId,
87578
        parent_computed: &Option<Vec<(CssPropertyType, CssPropertyWithOrigin)>>,
87578
        node_data: &[NodeData],
87578
        node_index: usize,
87578
    ) {
        // Step 2: Cascaded properties (UA CSS)
        {
87578
            let cascaded_slice = self.cascaded_props.get_slice(node_id.index());
197508
            for p in cascaded_slice.iter() {
197508
                if p.state == azul_css::dynamic_selector::PseudoStateType::Normal {
197508
                    if self.should_apply_cascaded(&ctx.computed_values, p.prop_type, &p.property) {
196982
                        self.process_property(ctx, &p.property, parent_computed);
196982
                    }
                }
            }
        }
        // Step 3: CSS properties (stylesheets)
        {
87578
            let css_slice = self.css_props.get_slice(node_id.index());
303113
            for p in css_slice.iter() {
296824
                if p.state == azul_css::dynamic_selector::PseudoStateType::Normal {
296824
                    self.process_property(ctx, &p.property, parent_computed);
296824
                }
            }
        }
        // Step 4: Inline CSS properties
87578
        for (prop, conds) in node_data[node_index].style.iter_inline_properties() {
            // Only apply unconditional (normal) properties
4655
            if conds.as_slice().is_empty() {
2394
                self.process_property(ctx, prop, parent_computed);
2394
            }
        }
        // Step 5: User-overridden properties
87578
        if let Some(user_props) = self.user_overridden_properties.get(node_id.index()) {
            for (_, prop) in user_props.iter() {
                self.process_property(ctx, &prop, parent_computed);
            }
87578
        }
87578
    }
    /// Check if a cascaded property should be applied
197508
    fn should_apply_cascaded(
197508
        &self,
197508
        computed: &[(CssPropertyType, CssPropertyWithOrigin)],
197508
        prop_type: CssPropertyType,
197508
        prop: &CssProperty,
197508
    ) -> bool {
        // Skip relative font-size if we already have inherited resolved value
197508
        if prop_type == CssPropertyType::FontSize {
12437
            if let Ok(idx) = computed.binary_search_by_key(&prop_type, |(k, _)| *k) {
12272
                if computed[idx].1.origin == CssPropertyOrigin::Inherited
12272
                    && Self::has_relative_font_size_unit(prop)
                {
526
                    return false;
11746
                }
165
            }
185071
        }
196982
        match computed.binary_search_by_key(&prop_type, |(k, _)| *k) {
162519
            Err(_) => true,
34463
            Ok(idx) => computed[idx].1.origin == CssPropertyOrigin::Inherited,
        }
197508
    }
    /// Process a single property: resolve and store
496200
    fn process_property(
496200
        &self,
496200
        ctx: &mut InheritanceContext,
496200
        prop: &CssProperty,
496200
        parent_computed: &Option<Vec<(CssPropertyType, CssPropertyWithOrigin)>>,
496200
    ) {
496200
        let prop_type = prop.get_type();
496200
        let resolved = if prop_type == CssPropertyType::FontSize {
25309
            self.resolve_font_size_property(prop, parent_computed)
        } else {
470891
            self.resolve_other_property(prop, &ctx.computed_values)
        };
496200
        let entry = (prop_type, CssPropertyWithOrigin {
496200
            property: resolved,
496200
            origin: CssPropertyOrigin::Own,
496200
        });
496200
        match ctx.computed_values.binary_search_by_key(&prop_type, |(k, _)| *k) {
41064
            Ok(idx) => ctx.computed_values[idx] = entry,
455136
            Err(idx) => ctx.computed_values.insert(idx, entry),
        }
496200
    }
    /// Resolve font-size property (uses parent's font-size as reference)
25309
    fn resolve_font_size_property(
25309
        &self,
25309
        prop: &CssProperty,
25309
        parent_computed: &Option<Vec<(CssPropertyType, CssPropertyWithOrigin)>>,
25309
    ) -> CssProperty {
25309
        let parent_font_size = parent_computed
25309
            .as_ref()
25309
            .and_then(|p| {
23542
                p.binary_search_by_key(&CssPropertyType::FontSize, |(k, _)| *k)
23542
                    .ok()
23542
                    .map(|idx| &p[idx].1)
23542
            });
25309
        match parent_font_size {
15523
            Some(pfs) => Self::resolve_property_dependency(prop, &pfs.property).unwrap_or_else(
                || {
                    Self::resolve_font_size_to_pixels(
                        prop,
                        azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
                    )
                },
            ),
9786
            None => Self::resolve_font_size_to_pixels(
9786
                prop,
                azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
            ),
        }
25309
    }
    /// Resolve other properties (uses current node's font-size as reference)
470891
    fn resolve_other_property(
470891
        &self,
470891
        prop: &CssProperty,
470891
        computed: &[(CssPropertyType, CssPropertyWithOrigin)],
470891
    ) -> CssProperty {
470891
        computed
470891
            .binary_search_by_key(&CssPropertyType::FontSize, |(k, _)| *k)
470891
            .ok()
470891
            .and_then(|idx| Self::resolve_property_dependency(prop, &computed[idx].1.property))
470891
            .unwrap_or_else(|| prop.clone())
470891
    }
    /// Convert font-size to absolute pixels
9786
    fn resolve_font_size_to_pixels(prop: &CssProperty, reference_px: f32) -> CssProperty {
        use azul_css::{
            css::CssPropertyValue,
            props::basic::{font::StyleFontSize, length::SizeMetric, pixel::PixelValue},
        };
9786
        let CssProperty::FontSize(css_val) = prop else {
            return prop.clone();
        };
9786
        let Some(font_size) = css_val.get_property() else {
            return prop.clone();
        };
9786
        let resolved_px = match font_size.inner.metric {
9583
            SizeMetric::Px => font_size.inner.number.get(),
            SizeMetric::Pt => font_size.inner.number.get() * PT_TO_PX,
            SizeMetric::In => font_size.inner.number.get() * IN_TO_PX,
            SizeMetric::Cm => font_size.inner.number.get() * CM_TO_PX,
            SizeMetric::Mm => font_size.inner.number.get() * MM_TO_PX,
203
            SizeMetric::Em => font_size.inner.number.get() * reference_px,
            SizeMetric::Rem => {
                font_size.inner.number.get() * azul_css::props::basic::pixel::DEFAULT_FONT_SIZE
            }
            SizeMetric::Percent => font_size.inner.number.get() / 100.0 * reference_px,
            SizeMetric::Vw | SizeMetric::Vh | SizeMetric::Vmin | SizeMetric::Vmax => {
                return prop.clone();
            }
        };
9786
        CssProperty::FontSize(CssPropertyValue::Exact(StyleFontSize {
9786
            inner: PixelValue::px(resolved_px),
9786
        }))
9786
    }
    /// Check if font-size has relative unit (em, rem, %)
12272
    fn has_relative_font_size_unit(prop: &CssProperty) -> bool {
        use azul_css::props::basic::length::SizeMetric;
12272
        let CssProperty::FontSize(css_val) = prop else {
            return false;
        };
12272
        css_val
12272
            .get_property()
12272
            .map(|fs| {
11746
                matches!(
12272
                    fs.inner.metric,
                    SizeMetric::Em | SizeMetric::Rem | SizeMetric::Percent
                )
12272
            })
12272
            .unwrap_or(false)
12272
    }
    /// Store computed values if changed, returns true if values were updated
87578
    fn store_if_changed(&mut self, ctx: &InheritanceContext) -> bool {
87578
        let values_changed = self
87578
            .computed_values
87578
            .get(ctx.node_id.index())
87578
            .map(|old| old != &ctx.computed_values)
87578
            .unwrap_or(true);
87578
        self.computed_values[ctx.node_id.index()] = ctx.computed_values.clone();
87578
        values_changed
87578
    }
}
/// Context for computing inherited values for a single node
struct InheritanceContext {
    node_id: NodeId,
    parent_id: Option<NodeId>,
    computed_values: Vec<(CssPropertyType, CssPropertyWithOrigin)>,
}
impl CssPropertyCache {
    /// Clear the entire compact cache. Call after major DOM changes.
    pub(crate) fn invalidate_resolved_cache(&mut self) {
        self.compact_cache = None;
    }
}