1extern crate alloc;
25
26use alloc::{boxed::Box, string::String, vec::Vec};
27use core::fmt::Write;
28use core::mem::ManuallyDrop;
29
30use crate::dom::NodeType;
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
35pub enum CssPropertyOrigin {
36 Inherited,
38 Own,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
44pub struct CssPropertyWithOrigin {
45 pub property: CssProperty,
46 pub origin: CssPropertyOrigin,
47}
48
49use azul_css::{
50 css::{Css, CssPath},
51 props::{
52 basic::{StyleFontFamily, StyleFontFamilyVec, StyleFontSize},
53 layout::{LayoutDisplay, LayoutHeight, LayoutWidth},
54 property::{
55 BoxDecorationBreakValue, BreakInsideValue, CaretAnimationDurationValue,
56 CaretColorValue, CaretWidthValue, ClipPathValue, ColumnCountValue, ColumnFillValue,
57 ColumnRuleColorValue, ColumnRuleStyleValue, ColumnRuleWidthValue, ColumnSpanValue,
58 ColumnWidthValue, ContentValue, CounterIncrementValue, CounterResetValue, CssProperty,
59 CssPropertyType, FlowFromValue, FlowIntoValue, LayoutAlignContentValue,
60 LayoutAlignItemsValue, LayoutAlignSelfValue, LayoutBorderBottomWidthValue,
61 LayoutBorderLeftWidthValue, LayoutBorderRightWidthValue, LayoutBorderSpacingValue,
62 LayoutBorderTopWidthValue, LayoutBoxSizingValue, LayoutClearValue,
63 LayoutColumnGapValue, LayoutDisplayValue, LayoutFlexBasisValue,
64 LayoutFlexDirectionValue, LayoutFlexGrowValue, LayoutFlexShrinkValue,
65 LayoutFlexWrapValue, LayoutFloatValue, LayoutGapValue, LayoutGridAutoColumnsValue,
66 LayoutGridAutoFlowValue, LayoutGridAutoRowsValue, LayoutGridColumnValue,
67 LayoutGridRowValue, LayoutGridTemplateColumnsValue, LayoutGridTemplateRowsValue,
68 LayoutHeightValue, LayoutInsetBottomValue, LayoutJustifyContentValue,
69 LayoutJustifyItemsValue, LayoutJustifySelfValue, LayoutLeftValue,
70 LayoutMarginBottomValue, LayoutMarginLeftValue, LayoutMarginRightValue,
71 LayoutMarginTopValue, LayoutMaxHeightValue, LayoutMaxWidthValue, LayoutMinHeightValue,
72 LayoutMinWidthValue, LayoutOverflowValue, LayoutPaddingBottomValue,
73 LayoutPaddingLeftValue, LayoutPaddingRightValue, LayoutPaddingTopValue,
74 LayoutPositionValue, LayoutRightValue, LayoutRowGapValue, LayoutScrollbarWidthValue,
75 LayoutTableLayoutValue, LayoutTextJustifyValue, LayoutTopValue, LayoutWidthValue,
76 LayoutWritingModeValue, LayoutZIndexValue, OrphansValue, PageBreakValue,
77 StyleBackgroundContentValue, ScrollbarFadeDelayValue, ScrollbarFadeDurationValue,
78 ScrollbarVisibilityModeValue, SelectionBackgroundColorValue, SelectionColorValue,
79 SelectionRadiusValue, ShapeImageThresholdValue, ShapeInsideValue, ShapeMarginValue,
80 ShapeOutsideValue, StringSetValue, StyleBackfaceVisibilityValue,
81 StyleBackgroundContentVecValue, StyleBackgroundPositionVecValue,
82 StyleBackgroundRepeatVecValue, StyleBackgroundSizeVecValue,
83 StyleBorderBottomColorValue, StyleBorderBottomLeftRadiusValue,
84 StyleBorderBottomRightRadiusValue, StyleBorderBottomStyleValue,
85 StyleBorderCollapseValue, StyleBorderLeftColorValue, StyleBorderLeftStyleValue,
86 StyleBorderRightColorValue, StyleBorderRightStyleValue, StyleBorderTopColorValue,
87 StyleBorderTopLeftRadiusValue, StyleBorderTopRightRadiusValue,
88 StyleBorderTopStyleValue, StyleBoxShadowValue, StyleCaptionSideValue, StyleCursorValue,
89 StyleDirectionValue, StyleEmptyCellsValue, StyleExclusionMarginValue,
90 StyleFilterVecValue, StyleFontFamilyVecValue, StyleFontSizeValue, StyleFontStyleValue,
91 StyleFontValue, StyleFontWeightValue, StyleHangingPunctuationValue,
92 StyleHyphenationLanguageValue, StyleHyphensValue, StyleInitialLetterValue,
93 StyleLetterSpacingValue, StyleLineBreakValue, StyleLineClampValue, StyleLineHeightValue,
94 StyleListStylePositionValue, StyleListStyleTypeValue, StyleMixBlendModeValue,
95 StyleAspectRatioValue, StyleObjectFitValue, StyleObjectPositionValue, StyleTextOverflowValue,
96 StyleOpacityValue, StylePerspectiveOriginValue,
97 StyleScrollbarColorValue, StyleOverflowWrapValue, StyleTabSizeValue,
98 StyleTextAlignLastValue, StyleTextOrientationValue, StyleTextTransformValue,
99 StyleTextAlignValue, StyleTextColorValue,
100 StyleTextCombineUprightValue, StyleUnicodeBidiValue,
101 StyleTextBoxTrimValue, StyleTextBoxEdgeValue,
102 StyleDominantBaselineValue, StyleAlignmentBaselineValue, StyleBaselineSourceValue,
103 StyleLineFitEdgeValue,
104 StyleInitialLetterAlignValue, StyleInitialLetterWrapValue,
105 StyleScrollbarGutterValue, StyleOverflowClipMarginValue, StyleClipRectValue,
106 StyleTextDecorationValue, StyleTextIndentValue,
107 StyleTransformOriginValue, StyleTransformVecValue, StyleUserSelectValue,
108 StyleVerticalAlignValue, StyleVisibilityValue, StyleWhiteSpaceValue,
109 StyleWordBreakValue, StyleWordSpacingValue, WidowsValue,
110 },
111 style::{StyleCursor, StyleTextColor, StyleTransformOrigin},
112 },
113 AzString,
114};
115
116use crate::{
117 dom::{NodeData, NodeId, TabIndex, TagId},
118 id::{NodeDataContainer, NodeDataContainerRef},
119 style::CascadeInfo,
120 styled_dom::{
121 NodeHierarchyItem, NodeHierarchyItemId, NodeHierarchyItemVec, ParentWithNodeDepth,
122 ParentWithNodeDepthVec, StyledNodeState, TagIdToNodeIdMapping,
123 },
124};
125
126use azul_css::dynamic_selector::{
127 CssPropertyWithConditions, CssPropertyWithConditionsVec, DynamicSelectorContext,
128};
129
130#[cfg(feature = "std")]
131std::thread_local! {
132 static PROP_COUNTS: core::cell::RefCell<
133 std::collections::HashMap<&'static str, usize>
134 > = core::cell::RefCell::new(std::collections::HashMap::new());
135}
136
137#[cfg(feature = "std")]
146#[must_use] pub fn drain_css_prop_counts() -> Vec<(&'static str, usize)> {
147 PROP_COUNTS
150 .try_with(|c| {
151 let map = core::mem::take(&mut *c.borrow_mut());
152 let mut v: Vec<_> = map.into_iter().collect();
153 v.sort_by(|a, b| b.1.cmp(&a.1));
154 v
155 })
156 .unwrap_or_default()
157}
158
159const PT_TO_PX: f32 = 1.333_333;
161const IN_TO_PX: f32 = 96.0;
162const CM_TO_PX: f32 = 37.795_277;
163const MM_TO_PX: f32 = 3.779_527_7;
164
165#[allow(unused_macros)]
167macro_rules! match_property_value {
168 ($property:expr, $value:ident, $expr:expr) => {
169 match $property {
170 CssProperty::CaretColor($value) => $expr,
171 CssProperty::CaretAnimationDuration($value) => $expr,
172 CssProperty::SelectionBackgroundColor($value) => $expr,
173 CssProperty::SelectionColor($value) => $expr,
174 CssProperty::SelectionRadius($value) => $expr,
175 CssProperty::TextColor($value) => $expr,
176 CssProperty::FontSize($value) => $expr,
177 CssProperty::FontFamily($value) => $expr,
178 CssProperty::FontWeight($value) => $expr,
179 CssProperty::FontStyle($value) => $expr,
180 CssProperty::TextAlign($value) => $expr,
181 CssProperty::TextJustify($value) => $expr,
182 CssProperty::VerticalAlign($value) => $expr,
183 CssProperty::LetterSpacing($value) => $expr,
184 CssProperty::TextIndent($value) => $expr,
185 CssProperty::InitialLetter($value) => $expr,
186 CssProperty::LineClamp($value) => $expr,
187 CssProperty::HangingPunctuation($value) => $expr,
188 CssProperty::TextCombineUpright($value) => $expr,
189 CssProperty::UnicodeBidi($value) => $expr,
190 CssProperty::TextBoxTrim($value) => $expr,
191 CssProperty::TextBoxEdge($value) => $expr,
192 CssProperty::DominantBaseline($value) => $expr,
193 CssProperty::AlignmentBaseline($value) => $expr,
194 CssProperty::BaselineSource($value) => $expr,
195 CssProperty::LineFitEdge($value) => $expr,
196 CssProperty::InitialLetterAlign($value) => $expr,
197 CssProperty::InitialLetterWrap($value) => $expr,
198 CssProperty::ScrollbarGutter($value) => $expr,
199 CssProperty::OverflowClipMargin($value) => $expr,
200 CssProperty::Clip($value) => $expr,
201 CssProperty::ExclusionMargin($value) => $expr,
202 CssProperty::HyphenationLanguage($value) => $expr,
203 CssProperty::LineHeight($value) => $expr,
204 CssProperty::WordSpacing($value) => $expr,
205 CssProperty::TabSize($value) => $expr,
206 CssProperty::WhiteSpace($value) => $expr,
207 CssProperty::Hyphens($value) => $expr,
208 CssProperty::Direction($value) => $expr,
209 CssProperty::UserSelect($value) => $expr,
210 CssProperty::TextDecoration($value) => $expr,
211 CssProperty::Cursor($value) => $expr,
212 CssProperty::Display($value) => $expr,
213 CssProperty::Float($value) => $expr,
214 CssProperty::BoxSizing($value) => $expr,
215 CssProperty::Width($value) => $expr,
216 CssProperty::Height($value) => $expr,
217 CssProperty::MinWidth($value) => $expr,
218 CssProperty::MinHeight($value) => $expr,
219 CssProperty::MaxWidth($value) => $expr,
220 CssProperty::MaxHeight($value) => $expr,
221 CssProperty::Position($value) => $expr,
222 CssProperty::Top($value) => $expr,
223 CssProperty::Right($value) => $expr,
224 CssProperty::Left($value) => $expr,
225 CssProperty::Bottom($value) => $expr,
226 CssProperty::ZIndex($value) => $expr,
227 CssProperty::FlexWrap($value) => $expr,
228 CssProperty::FlexDirection($value) => $expr,
229 CssProperty::FlexGrow($value) => $expr,
230 CssProperty::FlexShrink($value) => $expr,
231 CssProperty::FlexBasis($value) => $expr,
232 CssProperty::JustifyContent($value) => $expr,
233 CssProperty::AlignItems($value) => $expr,
234 CssProperty::AlignContent($value) => $expr,
235 CssProperty::AlignSelf($value) => $expr,
236 CssProperty::JustifyItems($value) => $expr,
237 CssProperty::JustifySelf($value) => $expr,
238 CssProperty::BackgroundContent($value) => $expr,
239 CssProperty::BackgroundPosition($value) => $expr,
240 CssProperty::BackgroundSize($value) => $expr,
241 CssProperty::BackgroundRepeat($value) => $expr,
242 CssProperty::OverflowX($value) => $expr,
243 CssProperty::OverflowY($value) => $expr,
244 CssProperty::OverflowBlock($value) => $expr,
245 CssProperty::OverflowInline($value) => $expr,
246 CssProperty::PaddingTop($value) => $expr,
247 CssProperty::PaddingLeft($value) => $expr,
248 CssProperty::PaddingRight($value) => $expr,
249 CssProperty::PaddingBottom($value) => $expr,
250 CssProperty::MarginTop($value) => $expr,
251 CssProperty::MarginLeft($value) => $expr,
252 CssProperty::MarginRight($value) => $expr,
253 CssProperty::MarginBottom($value) => $expr,
254 CssProperty::BorderTopLeftRadius($value) => $expr,
255 CssProperty::BorderTopRightRadius($value) => $expr,
256 CssProperty::BorderBottomLeftRadius($value) => $expr,
257 CssProperty::BorderBottomRightRadius($value) => $expr,
258 CssProperty::BorderTopColor($value) => $expr,
259 CssProperty::BorderRightColor($value) => $expr,
260 CssProperty::BorderLeftColor($value) => $expr,
261 CssProperty::BorderBottomColor($value) => $expr,
262 CssProperty::BorderTopStyle($value) => $expr,
263 CssProperty::BorderRightStyle($value) => $expr,
264 CssProperty::BorderLeftStyle($value) => $expr,
265 CssProperty::BorderBottomStyle($value) => $expr,
266 CssProperty::BorderTopWidth($value) => $expr,
267 CssProperty::BorderRightWidth($value) => $expr,
268 CssProperty::BorderLeftWidth($value) => $expr,
269 CssProperty::BorderBottomWidth($value) => $expr,
270 CssProperty::BoxShadow($value) => $expr,
271 CssProperty::Opacity($value) => $expr,
272 CssProperty::Transform($value) => $expr,
273 CssProperty::TransformOrigin($value) => $expr,
274 CssProperty::PerspectiveOrigin($value) => $expr,
275 CssProperty::BackfaceVisibility($value) => $expr,
276 CssProperty::MixBlendMode($value) => $expr,
277 CssProperty::Filter($value) => $expr,
278 CssProperty::Visibility($value) => $expr,
279 CssProperty::WritingMode($value) => $expr,
280 CssProperty::GridTemplateColumns($value) => $expr,
281 CssProperty::GridTemplateRows($value) => $expr,
282 CssProperty::GridAutoColumns($value) => $expr,
283 CssProperty::GridAutoRows($value) => $expr,
284 CssProperty::GridAutoFlow($value) => $expr,
285 CssProperty::GridColumn($value) => $expr,
286 CssProperty::GridRow($value) => $expr,
287 CssProperty::GridTemplateAreas($value) => $expr,
288 CssProperty::Gap($value) => $expr,
289 CssProperty::ColumnGap($value) => $expr,
290 CssProperty::RowGap($value) => $expr,
291 CssProperty::Clear($value) => $expr,
292 CssProperty::ScrollbarTrack($value) => $expr,
293 CssProperty::ScrollbarThumb($value) => $expr,
294 CssProperty::ScrollbarButton($value) => $expr,
295 CssProperty::ScrollbarCorner($value) => $expr,
296 CssProperty::ScrollbarResizer($value) => $expr,
297 CssProperty::ScrollbarWidth($value) => $expr,
298 CssProperty::ScrollbarColor($value) => $expr,
299 CssProperty::ListStyleType($value) => $expr,
300 CssProperty::ListStylePosition($value) => $expr,
301 CssProperty::Font($value) => $expr,
302 CssProperty::ColumnCount($value) => $expr,
303 CssProperty::ColumnWidth($value) => $expr,
304 CssProperty::ColumnSpan($value) => $expr,
305 CssProperty::ColumnFill($value) => $expr,
306 CssProperty::ColumnRuleStyle($value) => $expr,
307 CssProperty::ColumnRuleWidth($value) => $expr,
308 CssProperty::ColumnRuleColor($value) => $expr,
309 CssProperty::FlowInto($value) => $expr,
310 CssProperty::FlowFrom($value) => $expr,
311 CssProperty::ShapeOutside($value) => $expr,
312 CssProperty::ShapeInside($value) => $expr,
313 CssProperty::ShapeImageThreshold($value) => $expr,
314 CssProperty::ShapeMargin($value) => $expr,
315 CssProperty::ClipPath($value) => $expr,
316 CssProperty::Content($value) => $expr,
317 CssProperty::CounterIncrement($value) => $expr,
318 CssProperty::CounterReset($value) => $expr,
319 CssProperty::StringSet($value) => $expr,
320 CssProperty::Orphans($value) => $expr,
321 CssProperty::Widows($value) => $expr,
322 CssProperty::PageBreakBefore($value) => $expr,
323 CssProperty::PageBreakAfter($value) => $expr,
324 CssProperty::PageBreakInside($value) => $expr,
325 CssProperty::BreakInside($value) => $expr,
326 CssProperty::BoxDecorationBreak($value) => $expr,
327 CssProperty::TableLayout($value) => $expr,
328 CssProperty::BorderCollapse($value) => $expr,
329 CssProperty::BorderSpacing($value) => $expr,
330 CssProperty::CaptionSide($value) => $expr,
331 CssProperty::EmptyCells($value) => $expr,
332 }
333 };
334}
335
336#[derive(Debug, Clone, PartialEq, Eq)]
342pub struct StatefulCssProperty {
343 pub state: azul_css::dynamic_selector::PseudoStateType,
344 pub prop_type: CssPropertyType,
345 pub property: CssProperty,
346}
347
348#[derive(Debug, Clone)]
367pub struct FlatVecVec<T> {
368 build: Vec<Vec<T>>,
370 data: Vec<T>,
372 offsets: Vec<(u32, u32)>,
374}
375
376impl<T: PartialEq> PartialEq for FlatVecVec<T> {
377 fn eq(&self, other: &Self) -> bool {
378 let self_in_build = !self.build.is_empty() && self.offsets.is_empty();
379 let other_in_build = !other.build.is_empty() && other.offsets.is_empty();
380 debug_assert!(
381 self_in_build == other_in_build,
382 "FlatVecVec::eq called across phases (one build, one flattened)"
383 );
384 if self_in_build || other_in_build {
385 self.build == other.build
386 } else {
387 self.data == other.data && self.offsets == other.offsets
388 }
389 }
390}
391
392impl<T> Default for FlatVecVec<T> {
393 fn default() -> Self {
394 Self {
395 build: Vec::new(),
396 data: Vec::new(),
397 offsets: Vec::new(),
398 }
399 }
400}
401
402impl<T> FlatVecVec<T> {
403 #[must_use] pub fn heap_bytes(&self, per_element_size: usize) -> usize {
408 let data_bytes = self.data.capacity() * per_element_size;
409 let offsets_bytes =
410 self.offsets.capacity() * size_of::<(u32, u32)>();
411 let mut build_bytes = self.build.capacity() * size_of::<Vec<T>>();
412 for v in &self.build {
413 build_bytes += v.capacity() * per_element_size;
414 }
415 data_bytes + offsets_bytes + build_bytes
416 }
417
418 #[must_use] pub fn new(node_count: usize) -> Self {
420 let mut build = Vec::with_capacity(node_count);
421 for _ in 0..node_count {
422 build.push(Vec::new());
423 }
424 Self {
425 build,
426 data: Vec::new(),
427 offsets: Vec::new(),
428 }
429 }
430
431 #[inline]
436 pub fn push_to(&mut self, node_index: usize, item: T) {
437 self.build[node_index].push(item);
438 }
439
440 #[inline]
442 pub fn build_mut(&mut self, node_index: usize) -> &mut Vec<T> {
443 &mut self.build[node_index]
444 }
445
446 #[inline]
448 pub fn build_iter_mut(&mut self) -> core::slice::IterMut<'_, Vec<T>> {
449 self.build.iter_mut()
450 }
451
452 #[inline]
455 #[must_use] pub fn build_get(&self, node_index: usize) -> Option<&Vec<T>> {
456 self.build.get(node_index)
457 }
458
459 #[inline]
461 #[must_use] pub const fn len(&self) -> usize {
462 if self.offsets.is_empty() {
463 self.build.len()
464 } else {
465 self.offsets.len()
466 }
467 }
468
469 #[inline]
471 #[must_use] pub const fn is_empty(&self) -> bool {
472 self.len() == 0
473 }
474
475 #[inline]
477 #[must_use] pub const fn is_flattened(&self) -> bool {
478 !self.offsets.is_empty() || self.build.is_empty()
479 }
480
481 #[inline]
485 #[must_use] pub fn get_slice(&self, node_index: usize) -> &[T] {
486 if self.offsets.is_empty() {
487 self.build.get(node_index).map_or(&[], alloc::vec::Vec::as_slice)
489 } else {
490 if let Some(&(start, len)) = self.offsets.get(node_index) {
492 let s = start as usize;
493 let l = len as usize;
494 &self.data[s..s + l]
495 } else {
496 &[]
497 }
498 }
499 }
500
501 pub fn sort_each_and_flatten<K: Ord + Eq>(&mut self, key_fn: impl Fn(&T) -> K) {
506 let node_count = self.build.len();
507 let total: usize = self.build.iter().map(alloc::vec::Vec::len).sum();
508
509 let mut flat_data = Vec::with_capacity(total);
510 let mut offsets = Vec::with_capacity(node_count);
511
512 for inner in &mut self.build {
513 inner.sort_by_key(|a| key_fn(a));
514
515 let n = inner.len();
517 let mut keep = vec![false; n];
518 for i in 0..n {
519 if i + 1 >= n || key_fn(&inner[i]) != key_fn(&inner[i + 1]) {
520 keep[i] = true;
521 }
522 }
523
524 let start = u32::try_from(flat_data.len()).unwrap_or(u32::MAX);
525 for (i, item) in inner.drain(..).enumerate() {
527 if keep[i] {
528 flat_data.push(item);
529 }
530 }
531
532 let len = u32::try_from(flat_data.len()).unwrap_or(u32::MAX) - start;
533 offsets.push((start, len));
534 }
535
536 flat_data.shrink_to_fit();
537 self.data = flat_data;
538 self.offsets = offsets;
539 self.build = Vec::new();
540 }
541
542 pub fn flatten(&mut self) {
544 let node_count = self.build.len();
545 let total: usize = self.build.iter().map(alloc::vec::Vec::len).sum();
546
547 let mut flat_data = Vec::with_capacity(total);
548 let mut offsets = Vec::with_capacity(node_count);
549
550 for inner in &mut self.build {
551 let start = u32::try_from(flat_data.len()).unwrap_or(u32::MAX);
552 let len = u32::try_from(inner.len()).unwrap_or(u32::MAX);
553 offsets.push((start, len));
554 flat_data.append(inner);
555 }
556
557 self.data = flat_data;
558 self.offsets = offsets;
559 self.build = Vec::new();
560 }
561
562 pub fn retain(&mut self, predicate: impl Fn(&T) -> bool) where T: Clone {
565 if self.offsets.is_empty() { return; }
566 let node_count = self.offsets.len();
567 let mut new_data = Vec::new();
568 let mut new_offsets = Vec::with_capacity(node_count);
569 for &(start, len) in &self.offsets {
570 let s = start as usize;
571 let l = len as usize;
572 let new_start = u32::try_from(new_data.len()).unwrap_or(u32::MAX);
573 let slice = &self.data[s..s + l];
574 let mut kept = 0u32;
575 for item in slice {
576 if predicate(item) {
577 new_data.push((*item).clone());
578 kept += 1;
579 }
580 }
581 new_offsets.push((new_start, kept));
582 }
583 new_data.shrink_to_fit();
584 self.data = new_data;
585 self.offsets = new_offsets;
586 }
587
588 pub fn ensure_build_phase(&mut self) where T: Clone {
597 if self.offsets.is_empty() {
598 return; }
600 let mut build = Vec::with_capacity(self.offsets.len());
601 for &(start, len) in &self.offsets {
602 let s = start as usize;
603 let l = len as usize;
604 build.push(self.data[s..s + l].to_vec());
605 }
606 self.build = build;
607 self.data = Vec::new();
608 self.offsets = Vec::new();
609 }
610
611 pub fn retain_with_node_index(
614 &mut self,
615 predicate: impl Fn(usize, &T) -> bool,
616 ) where T: Clone {
617 if self.offsets.is_empty() { return; }
618 let node_count = self.offsets.len();
619 let mut new_data = Vec::new();
620 let mut new_offsets = Vec::with_capacity(node_count);
621 for (node_idx, &(start, len)) in self.offsets.iter().enumerate() {
622 let s = start as usize;
623 let l = len as usize;
624 let new_start = u32::try_from(new_data.len()).unwrap_or(u32::MAX);
625 let slice = &self.data[s..s + l];
626 let mut kept = 0u32;
627 for item in slice {
628 if predicate(node_idx, item) {
629 new_data.push((*item).clone());
630 kept += 1;
631 }
632 }
633 new_offsets.push((new_start, kept));
634 }
635 new_data.shrink_to_fit();
636 self.data = new_data;
637 self.offsets = new_offsets;
638 }
639
640 pub(crate) const fn iter_node_slices(&self) -> FlatVecVecIter<'_, T> {
643 FlatVecVecIter {
644 fvv: self,
645 idx: 0,
646 count: self.len(),
647 }
648 }
649
650 pub fn extend_from(&mut self, other: &mut Self) {
653 if !self.offsets.is_empty() && !other.offsets.is_empty() {
654 let base = u32::try_from(self.data.len()).unwrap_or(u32::MAX);
656 self.data.append(&mut other.data);
657 self.offsets.extend(other.offsets.drain(..).map(|(s, l)| (s + base, l)));
658 } else {
659 self.build.append(&mut other.build);
661 self.data.clear();
663 self.offsets.clear();
664 }
665 }
666}
667
668pub(crate) struct FlatVecVecIter<'a, T> {
670 fvv: &'a FlatVecVec<T>,
671 idx: usize,
672 count: usize,
673}
674
675impl<'a, T> Iterator for FlatVecVecIter<'a, T> {
676 type Item = (usize, &'a [T]);
677
678 #[inline]
679 fn next(&mut self) -> Option<Self::Item> {
680 if self.idx >= self.count {
681 return None;
682 }
683 let i = self.idx;
684 self.idx += 1;
685 Some((i, self.fvv.get_slice(i)))
686 }
687
688 fn size_hint(&self) -> (usize, Option<usize>) {
689 let rem = self.count - self.idx;
690 (rem, Some(rem))
691 }
692}
693
694impl<T> ExactSizeIterator for FlatVecVecIter<'_, T> {}
695
696#[derive(Debug, Default, Clone, PartialEq)]
708pub struct CssPropertyCache {
709 pub node_count: usize,
711
712 pub retained_author_css: Css,
719
720 pub user_overridden_properties: Vec<Vec<(CssPropertyType, CssProperty)>>,
722
723 pub cascaded_props: FlatVecVec<StatefulCssProperty>,
727
728 pub css_props: FlatVecVec<StatefulCssProperty>,
731
732 pub computed_values: Vec<Vec<(CssPropertyType, CssPropertyWithOrigin)>>,
734
735 pub compact_cache: Option<azul_css::compact_cache::CompactLayoutCache>,
739
740 pub global_css_props: Vec<CssProperty>,
744
745 pub resolved_font_sizes_px: crate::sync::OnceLock<Vec<f32>>,
759}
760
761#[derive(Debug, Clone, Copy, Default)]
769pub struct CssPropertyCacheBreakdown {
770 pub node_count: usize,
771 pub cascaded_props_bytes: usize,
772 pub css_props_bytes: usize,
773 pub computed_values_bytes: usize,
774 pub user_overridden_bytes: usize,
775 pub global_css_props_bytes: usize,
776 pub compact_cache_bytes: usize,
777 pub resolved_font_sizes_bytes: usize,
778}
779
780impl CssPropertyCacheBreakdown {
781 #[must_use] pub const fn total_bytes(&self) -> usize {
783 self.cascaded_props_bytes
784 + self.css_props_bytes
785 + self.computed_values_bytes
786 + self.user_overridden_bytes
787 + self.global_css_props_bytes
788 + self.compact_cache_bytes
789 + self.resolved_font_sizes_bytes
790 }
791}
792
793impl CssPropertyCache {
794 pub fn memory_breakdown(&self) -> CssPropertyCacheBreakdown {
808 let stateful_sz = size_of::<StatefulCssProperty>();
809 let computed_entry_sz =
810 size_of::<(CssPropertyType, CssPropertyWithOrigin)>();
811 let outer_vec_sz = size_of::<Vec<(CssPropertyType, CssPropertyWithOrigin)>>();
812
813 let cascaded_bytes = self.cascaded_props.heap_bytes(stateful_sz);
814 let css_bytes = self.css_props.heap_bytes(stateful_sz);
815
816 let mut computed_bytes = self.computed_values.capacity() * outer_vec_sz;
817 for v in &self.computed_values {
818 computed_bytes += v.capacity() * computed_entry_sz;
819 }
820
821 let user_overridden_bytes = {
822 let mut b = self.user_overridden_properties.capacity() * outer_vec_sz;
823 for v in &self.user_overridden_properties {
824 b += v.capacity()
825 * size_of::<(CssPropertyType, CssProperty)>();
826 }
827 b
828 };
829
830 let global_bytes = self.global_css_props.capacity()
831 * size_of::<CssProperty>();
832
833 let compact_bytes = self
834 .compact_cache
835 .as_ref()
836 .map_or(0, |c| {
837 c.tier1_enums.capacity() * 8
838 + c.tier2_dims.capacity() * 68
839 + c.tier2_cold.capacity() * 28
840 + c.tier2b_text.capacity() * 24
841 + c.prev_font_hashes.capacity() * 8
842 + c.font_dirty_nodes.capacity() * 8
843 });
844
845 let resolved_font_sizes_bytes = self
846 .resolved_font_sizes_px
847 .get()
848 .map_or(0, |v| v.capacity() * size_of::<f32>());
849
850 CssPropertyCacheBreakdown {
851 node_count: self.node_count,
852 cascaded_props_bytes: cascaded_bytes,
853 css_props_bytes: css_bytes,
854 computed_values_bytes: computed_bytes,
855 user_overridden_bytes,
856 global_css_props_bytes: global_bytes,
857 compact_cache_bytes: compact_bytes,
858 resolved_font_sizes_bytes,
859 }
860 }
861
862 pub fn prune_compact_normal_props(&mut self) {
868 use azul_css::dynamic_selector::PseudoStateType;
869
870 #[cfg(feature = "std")]
871 {
872 static PRUNE_DBG: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
873 let dbg = *PRUNE_DBG.get_or_init(crate::profile::memory_enabled);
874 if dbg {
875 let mut normal_compact = 0usize;
876 let mut normal_noncompact = 0usize;
877 let mut nonnormal = 0usize;
878 for i in 0..self.css_props.len() {
879 for p in self.css_props.get_slice(i) {
880 if p.state != PseudoStateType::Normal {
881 nonnormal += 1;
882 } else if p.prop_type.has_compact_encoding() {
883 normal_compact += 1;
884 } else {
885 normal_noncompact += 1;
886 }
887 }
888 }
889 let ssp_sz = size_of::<StatefulCssProperty>();
890 let mut casc_normal_compact = 0usize;
891 let mut casc_total = 0usize;
892 for i in 0..self.cascaded_props.len() {
893 for p in self.cascaded_props.get_slice(i) {
894 casc_total += 1;
895 if p.state == PseudoStateType::Normal && p.prop_type.has_compact_encoding() {
896 casc_normal_compact += 1;
897 }
898 }
899 }
900 eprintln!("[PRUNE] css_props: norm+compact={normal_compact} norm+other={normal_noncompact} nonnorm={nonnormal} SSP={ssp_sz}B | cascaded: total={casc_total} norm+compact={casc_normal_compact}");
901 }
902 }
903
904 let keep = |p: &StatefulCssProperty| -> bool {
910 if p.state != PseudoStateType::Normal {
911 return true;
912 }
913 if !p.prop_type.has_compact_encoding() {
914 return true;
915 }
916 if property_needs_slow_path_after_compact(&p.property) {
919 return true;
920 }
921 false
922 };
923 if !self.cascaded_props.is_flattened() {
937 self.cascaded_props.sort_each_and_flatten(|p| (p.state, p.prop_type));
938 }
939 self.cascaded_props.retain(keep);
940 }
941
942 #[inline]
945 #[allow(clippy::trivially_copy_pass_by_ref)]
950 fn find_in_stateful<'a>(
951 props: &'a [StatefulCssProperty],
952 state: azul_css::dynamic_selector::PseudoStateType,
953 prop_type: &CssPropertyType,
954 ) -> Option<&'a CssProperty> {
955 let key = (state, *prop_type);
956 props.binary_search_by_key(&key, |p| (p.state, p.prop_type))
957 .ok()
958 .map(|idx| &props[idx].property)
959 }
960
961 #[inline]
964 fn has_state_props(
965 props: &[StatefulCssProperty],
966 state: azul_css::dynamic_selector::PseudoStateType,
967 ) -> bool {
968 let i = props.partition_point(|p| p.state < state);
971 i < props.len() && props[i].state == state
972 }
973
974 pub(crate) fn prop_types_for_state(
976 props: &[StatefulCssProperty],
977 state: azul_css::dynamic_selector::PseudoStateType,
978 ) -> impl Iterator<Item = &CssPropertyType> + '_ {
979 props.iter().filter(move |p| p.state == state).map(|p| &p.prop_type)
980 }
981}
982
983fn property_needs_slow_path_after_compact(prop: &CssProperty) -> bool {
993 use azul_css::css::CssPropertyValue;
994 use azul_css::props::{
995 basic::length::SizeMetric,
996 layout::{
997 dimensions::{LayoutHeight, LayoutWidth},
998 flex::LayoutFlexBasis,
999 },
1000 };
1001
1002 macro_rules! check_plain {
1004 ($v:expr) => {{
1005 if let CssPropertyValue::Exact(ref inner) = $v {
1006 return inner.inner.metric != SizeMetric::Px;
1007 }
1008 false
1009 }};
1010 }
1011
1012 match prop {
1013 CssProperty::Width(v) => {
1017 if let CssPropertyValue::Exact(LayoutWidth::Px(pv)) = v {
1018 return pv.metric != SizeMetric::Px;
1019 }
1020 false
1021 }
1022 CssProperty::Height(v) => {
1023 if let CssPropertyValue::Exact(LayoutHeight::Px(pv)) = v {
1024 return pv.metric != SizeMetric::Px;
1025 }
1026 false
1027 }
1028
1029 CssProperty::FlexBasis(v) => {
1031 if let CssPropertyValue::Exact(LayoutFlexBasis::Exact(pv)) = v {
1032 return pv.metric != SizeMetric::Px;
1033 }
1034 false
1035 }
1036
1037 CssProperty::MinWidth(v) => check_plain!(v),
1039 CssProperty::MaxWidth(v) => check_plain!(v),
1040 CssProperty::MinHeight(v) => check_plain!(v),
1041 CssProperty::MaxHeight(v) => check_plain!(v),
1042 CssProperty::FontSize(v) => check_plain!(v),
1043 CssProperty::PaddingTop(v) => check_plain!(v),
1044 CssProperty::PaddingRight(v) => check_plain!(v),
1045 CssProperty::PaddingBottom(v) => check_plain!(v),
1046 CssProperty::PaddingLeft(v) => check_plain!(v),
1047 CssProperty::MarginTop(v) => check_plain!(v),
1048 CssProperty::MarginRight(v) => check_plain!(v),
1049 CssProperty::MarginBottom(v) => check_plain!(v),
1050 CssProperty::MarginLeft(v) => check_plain!(v),
1051 CssProperty::BorderTopWidth(v) => check_plain!(v),
1052 CssProperty::BorderRightWidth(v) => check_plain!(v),
1053 CssProperty::BorderBottomWidth(v) => check_plain!(v),
1054 CssProperty::BorderLeftWidth(v) => check_plain!(v),
1055 CssProperty::Top(v) => check_plain!(v),
1056 CssProperty::Right(v) => check_plain!(v),
1057 CssProperty::Bottom(v) => check_plain!(v),
1058 CssProperty::Left(v) => check_plain!(v),
1059 CssProperty::ColumnGap(v) => check_plain!(v),
1060 CssProperty::RowGap(v) => check_plain!(v),
1061 CssProperty::LetterSpacing(v) => check_plain!(v),
1062 CssProperty::WordSpacing(v) => check_plain!(v),
1063 CssProperty::TextIndent(v) => check_plain!(v),
1064 CssProperty::TabSize(v) => check_plain!(v),
1065
1066 _ => false,
1068 }
1069}
1070
1071fn is_resolved_parent_inherited(prop_type: CssPropertyType) -> bool {
1096 prop_type == CssPropertyType::FontSize
1097}
1098
1099fn clone_inheritable_property(
1100 p: &CssProperty,
1101) -> CssProperty {
1102 use azul_css::props::property::CssProperty;
1103 if let CssProperty::FontFamily(v) = p { return CssProperty::FontFamily(v.clone()); }
1104 if let CssProperty::BackgroundContent(v) = p { return CssProperty::BackgroundContent(v.clone()); }
1105 if let CssProperty::BackgroundPosition(v) = p { return CssProperty::BackgroundPosition(v.clone()); }
1106 if let CssProperty::BackgroundSize(v) = p { return CssProperty::BackgroundSize(v.clone()); }
1107 if let CssProperty::BackgroundRepeat(v) = p { return CssProperty::BackgroundRepeat(v.clone()); }
1108 if let CssProperty::BoxShadowLeft(v) = p { return CssProperty::BoxShadowLeft(v.clone()); }
1109 if let CssProperty::BoxShadowRight(v) = p { return CssProperty::BoxShadowRight(v.clone()); }
1110 if let CssProperty::BoxShadowTop(v) = p { return CssProperty::BoxShadowTop(v.clone()); }
1111 if let CssProperty::BoxShadowBottom(v) = p { return CssProperty::BoxShadowBottom(v.clone()); }
1112 if let CssProperty::TextShadow(v) = p { return CssProperty::TextShadow(v.clone()); }
1113 if let CssProperty::ScrollbarTrack(v) = p { return CssProperty::ScrollbarTrack(v.clone()); }
1114 if let CssProperty::ScrollbarThumb(v) = p { return CssProperty::ScrollbarThumb(v.clone()); }
1115 if let CssProperty::ScrollbarButton(v) = p { return CssProperty::ScrollbarButton(v.clone()); }
1116 if let CssProperty::ScrollbarCorner(v) = p { return CssProperty::ScrollbarCorner(v.clone()); }
1117 if let CssProperty::ScrollbarResizer(v) = p { return CssProperty::ScrollbarResizer(v.clone()); }
1118 if let CssProperty::Transform(v) = p { return CssProperty::Transform(v.clone()); }
1119 if let CssProperty::Filter(v) = p { return CssProperty::Filter(v.clone()); }
1120 if let CssProperty::BackdropFilter(v) = p { return CssProperty::BackdropFilter(v.clone()); }
1121 if let CssProperty::Content(v) = p { return CssProperty::Content(v.clone()); }
1122 if let CssProperty::HyphenationLanguage(v) = p { return CssProperty::HyphenationLanguage(v.clone()); }
1123 if let CssProperty::Cursor(v) = p { return CssProperty::Cursor(*v); }
1124 p.clone()
1125}
1126
1127impl CssPropertyCache {
1128 #[must_use]
1132 #[allow(clippy::too_many_lines, clippy::cognitive_complexity)] pub fn restyle(
1134 &mut self,
1135 css: &mut Css,
1136 node_data: &NodeDataContainerRef<'_, NodeData>,
1137 node_hierarchy: &NodeHierarchyItemVec,
1138 non_leaf_nodes: &ParentWithNodeDepthVec,
1139 html_tree: &NodeDataContainerRef<'_, CascadeInfo>,
1140 ) -> Vec<TagIdToNodeIdMapping> {
1141 use azul_css::{
1142 css::{CssDeclaration, CssPathPseudoSelector::{Hover, Active, Focus, Dragging, DragOver}, CssPathSelector, CssRuleBlock},
1143 dynamic_selector::{DynamicSelector, PseudoStateType},
1144 props::layout::LayoutDisplay,
1145 };
1146
1147 let css_is_empty = css.is_empty();
1148
1149 if !css_is_empty {
1150 css.sort_by_specificity();
1151
1152 let mut global_only_rules: Vec<&CssRuleBlock> = Vec::new();
1157 let mut specific_rules: Vec<&CssRuleBlock> = Vec::new();
1158
1159 for rule in css.rules() {
1160 let selectors = rule.path.selectors.as_ref();
1161 let is_global_only = selectors.len() == 1
1162 && matches!(selectors.first(), Some(CssPathSelector::Global));
1163 if is_global_only {
1164 global_only_rules.push(rule);
1165 } else {
1166 specific_rules.push(rule);
1167 }
1168 }
1169
1170 let node_count = self.css_props.len();
1183 self.css_props = FlatVecVec::new(node_count);
1184 self.cascaded_props.ensure_build_phase();
1185
1186 self.global_css_props.clear();
1191 for rule in &global_only_rules {
1192 if crate::style::rule_ends_with(&rule.path, None) {
1193 for d in &rule.declarations {
1194 if let CssDeclaration::Static(s) = d {
1195 self.global_css_props.push(s.clone());
1196 }
1197 }
1198 }
1199 }
1200
1201 if !specific_rules.is_empty() {
1203
1204 macro_rules! filter_rules {($expected_pseudo_selector:expr, $node_id:expr) => {{
1214 let mut out: Vec<(u16, u16)> = Vec::new();
1215 for (rule_idx, rule_block) in specific_rules.iter().enumerate() {
1216 if !crate::style::rule_ends_with(&rule_block.path, $expected_pseudo_selector) {
1217 continue;
1218 }
1219 if !crate::style::matches_html_element(
1220 &rule_block.path,
1221 $node_id,
1222 &node_hierarchy.as_container(),
1223 &node_data,
1224 &html_tree,
1225 $expected_pseudo_selector,
1226 ) {
1227 continue;
1228 }
1229 for (decl_idx, decl) in rule_block.declarations.as_slice().iter().enumerate() {
1230 if matches!(decl, CssDeclaration::Static(_)) {
1231 out.push((u16::try_from(rule_idx).unwrap_or(u16::MAX), u16::try_from(decl_idx).unwrap_or(u16::MAX)));
1232 }
1233 }
1234 }
1235 out
1236 }};}
1237
1238 let has_normal = specific_rules.iter().any(|r| crate::style::rule_ends_with(&r.path, None));
1242 let has_hover = specific_rules.iter().any(|r| crate::style::rule_ends_with(&r.path, Some(Hover)));
1243 let has_active = specific_rules.iter().any(|r| crate::style::rule_ends_with(&r.path, Some(Active)));
1244 let has_focus = specific_rules.iter().any(|r| crate::style::rule_ends_with(&r.path, Some(Focus)));
1245 let has_dragging = specific_rules.iter().any(|r| crate::style::rule_ends_with(&r.path, Some(Dragging)));
1246 let has_drag_over = specific_rules.iter().any(|r| crate::style::rule_ends_with(&r.path, Some(DragOver)));
1247
1248 macro_rules! collect_and_assign {
1249 ($pseudo:expr, $state:expr, $has_any:expr) => {
1250 if $has_any {
1251 let indices: NodeDataContainer<(NodeId, Vec<(u16, u16)>)> = node_data
1252 .transform_nodeid_optional(|node_id| {
1253 let r = filter_rules!($pseudo, node_id);
1254 if r.is_empty() { None } else { Some((node_id, r)) }
1255 });
1256 for (n, pairs) in indices.internal.into_iter() {
1257 for (rule_idx, decl_idx) in pairs {
1258 let decl = &specific_rules[rule_idx as usize]
1259 .declarations
1260 .as_slice()[decl_idx as usize];
1261 if let CssDeclaration::Static(prop) = decl {
1262 self.css_props.push_to(n.index(), StatefulCssProperty {
1263 state: $state,
1264 prop_type: prop.get_type(),
1265 property: prop.clone(),
1266 });
1267 }
1268 }
1269 }
1270 }
1271 };
1272 }
1273
1274 collect_and_assign!(None, PseudoStateType::Normal, has_normal);
1275 collect_and_assign!(Some(Hover), PseudoStateType::Hover, has_hover);
1276 collect_and_assign!(Some(Active), PseudoStateType::Active, has_active);
1277 collect_and_assign!(Some(Focus), PseudoStateType::Focus, has_focus);
1278 collect_and_assign!(Some(Dragging), PseudoStateType::Dragging, has_dragging);
1279 collect_and_assign!(Some(DragOver), PseudoStateType::DragOver, has_drag_over);
1280
1281 } }
1283
1284 for ParentWithNodeDepth { depth: _, node_id } in non_leaf_nodes {
1287 let Some(parent_id) = node_id.into_crate_internal() else {
1288 continue;
1289 };
1290
1291 let all_states = [
1292 PseudoStateType::Normal,
1293 PseudoStateType::Hover,
1294 PseudoStateType::Active,
1295 PseudoStateType::Focus,
1296 PseudoStateType::Dragging,
1297 PseudoStateType::DragOver,
1298 ];
1299
1300 for &state in &all_states {
1301 let parent_inheritable_inline: Vec<(CssPropertyType, CssProperty)> = node_data[parent_id]
1303 .style
1304 .iter_inline_properties()
1305 .filter(|(_prop, conds)| {
1306 let conditions = conds.as_slice();
1307 if conditions.is_empty() {
1308 state == PseudoStateType::Normal
1309 } else {
1310 conditions.iter().all(|c| {
1311 matches!(c, DynamicSelector::PseudoState(s) if *s == state)
1312 })
1313 }
1314 })
1315 .map(|(prop, _)| prop)
1316 .filter(|prop| prop.get_type().is_inheritable() && !is_resolved_parent_inherited(prop.get_type()))
1317 .map(|p| (p.get_type(), clone_inheritable_property(p)))
1318 .collect();
1319
1320 let parent_inheritable_css: Vec<(CssPropertyType, CssProperty)> = if css_is_empty {
1322 Vec::new()
1323 } else {
1324 self.css_props.get_slice(parent_id.index())
1325 .iter()
1326 .filter(|p| p.state == state && p.prop_type.is_inheritable() && !is_resolved_parent_inherited(p.prop_type))
1327 .map(|p| (p.prop_type, clone_inheritable_property(&p.property)))
1328 .collect()
1329 };
1330
1331 let parent_inheritable_cascaded: Vec<(CssPropertyType, CssProperty)> =
1333 self.cascaded_props.get_slice(parent_id.index())
1334 .iter()
1335 .filter(|p| p.state == state && p.prop_type.is_inheritable() && !is_resolved_parent_inherited(p.prop_type))
1336 .map(|p| (p.prop_type, clone_inheritable_property(&p.property)))
1337 .collect();
1338
1339 if parent_inheritable_inline.is_empty()
1342 && parent_inheritable_css.is_empty()
1343 && parent_inheritable_cascaded.is_empty()
1344 {
1345 continue;
1346 }
1347
1348 for child_id in parent_id.az_children(&node_hierarchy.as_container()) {
1349 let child_vec = self.cascaded_props.build_mut(child_id.index());
1350 for (prop_type, prop_value) in parent_inheritable_inline
1351 .iter()
1352 .chain(parent_inheritable_css.iter())
1353 .chain(parent_inheritable_cascaded.iter())
1354 {
1355 if !child_vec.iter().any(|p| p.state == state && p.prop_type == *prop_type) {
1357 child_vec.push(StatefulCssProperty {
1358 state,
1359 prop_type: *prop_type,
1360 property: prop_value.clone(),
1361 });
1362 }
1363 }
1364 }
1365 }
1366 }
1367
1368 self.css_props.sort_each_and_flatten(|p| (p.state, p.prop_type));
1371
1372 self.invalidate_resolved_font_sizes();
1375
1376 self.generate_tag_ids(node_data, node_hierarchy)
1377 }
1378
1379 pub fn generate_tag_ids(
1383 &self,
1384 node_data: &NodeDataContainerRef<'_, NodeData>,
1385 node_hierarchy: &NodeHierarchyItemVec,
1386 ) -> Vec<TagIdToNodeIdMapping> {
1387
1388 use azul_css::compact_cache::{
1392 DISPLAY_SHIFT, DISPLAY_MASK,
1393 OVERFLOW_X_SHIFT, OVERFLOW_Y_SHIFT, OVERFLOW_MASK,
1394 };
1395
1396 let compact_cache = self.compact_cache.as_ref();
1397 let node_data_container = &node_data.internal;
1398
1399 let tag_ids = node_data
1400 .internal
1401 .iter()
1402 .enumerate()
1403 .filter_map(|(node_idx, node_data)| {
1404 let node_id = NodeId::new(node_idx);
1405
1406 let should_auto_insert_tabindex = node_data
1407 .get_callbacks()
1408 .iter()
1409 .any(|cb| cb.event.is_focus_callback());
1410
1411 let tab_index = node_data.get_tab_index().map_or(if should_auto_insert_tabindex {
1412 Some(TabIndex::Auto)
1413 } else {
1414 None
1415 }, Some);
1416
1417 let mut need_tag = false;
1418
1419 'compute_need_tag: {
1423 if let Some(cc) = compact_cache.as_ref() {
1425 let t1 = cc.tier1_enums[node_idx];
1426 let display_val = ((t1 >> DISPLAY_SHIFT) & DISPLAY_MASK) as u8;
1427 if display_val == 4 { break 'compute_need_tag; } }
1429
1430 if node_data.has_context_menu() || node_data.get_context_menu().is_some() {
1431 need_tag = true; break 'compute_need_tag;
1432 }
1433 if tab_index.is_some() { need_tag = true; break 'compute_need_tag; }
1434
1435 {
1437 use azul_css::dynamic_selector::{DynamicSelector, PseudoStateType};
1438 let has_pseudo = |state: PseudoStateType| -> bool {
1439 node_data.style.iter_inline_properties().any(|(_p, conds)| {
1440 conds.as_slice().iter().any(|c|
1441 matches!(c, DynamicSelector::PseudoState(s) if *s == state)
1442 )
1443 }) || Self::has_state_props(self.css_props.get_slice(node_idx), state)
1444 };
1445
1446 if has_pseudo(PseudoStateType::Hover)
1447 || has_pseudo(PseudoStateType::Active)
1448 || has_pseudo(PseudoStateType::Focus)
1449 || has_pseudo(PseudoStateType::Dragging)
1450 || has_pseudo(PseudoStateType::DragOver)
1451 {
1452 need_tag = true; break 'compute_need_tag;
1453 }
1454 }
1455
1456 let has_non_window_cb = !node_data.get_callbacks().is_empty()
1458 && !node_data.get_callbacks().iter().all(|cb| cb.event.is_window_callback());
1459 if has_non_window_cb { need_tag = true; break 'compute_need_tag; }
1460
1461 if self.css_props.get_slice(node_idx).iter().any(|p|
1463 p.state == azul_css::dynamic_selector::PseudoStateType::Normal
1464 && p.prop_type == CssPropertyType::Cursor
1465 ) || node_data.style.iter_inline_properties().any(|(p, _)|
1466 p.get_type() == CssPropertyType::Cursor
1467 ) {
1468 need_tag = true; break 'compute_need_tag;
1469 }
1470
1471 if let Some(cc) = compact_cache.as_ref() {
1473 let t1 = cc.tier1_enums[node_idx];
1474 let ox = ((t1 >> OVERFLOW_X_SHIFT) & OVERFLOW_MASK) as u8;
1475 let oy = ((t1 >> OVERFLOW_Y_SHIFT) & OVERFLOW_MASK) as u8;
1476 if ox == 2 || ox == 3 || oy == 2 || oy == 3 {
1478 need_tag = true; break 'compute_need_tag;
1479 }
1480 }
1481
1482 {
1484 use crate::dom::NodeType;
1485 let hier = node_hierarchy.as_container()[node_id];
1486 let mut has_text = false;
1487 if let Some(first_child) = hier.first_child_id(node_id) {
1488 let mut child_id = Some(first_child);
1489 while let Some(cid) = child_id {
1490 if matches!(node_data_container[cid.index()].get_node_type(), NodeType::Text(_)) {
1491 has_text = true; break;
1492 }
1493 child_id = node_hierarchy.as_container()[cid].next_sibling_id();
1494 }
1495 }
1496 if has_text { need_tag = true; break 'compute_need_tag; }
1497 }
1498
1499 break 'compute_need_tag;
1500 }
1501
1502 if need_tag {
1503 Some(TagIdToNodeIdMapping {
1504 tag_id: TagId::from_crate_internal(TagId::unique()),
1505 node_id: NodeHierarchyItemId::from_crate_internal(Some(node_id)),
1506 tab_index: tab_index.into(),
1507 })
1508 } else {
1509 None
1510 }
1511 })
1512 .collect::<Vec<_>>();
1513
1514 tag_ids
1515 }
1516
1517 #[allow(clippy::too_many_lines, clippy::cognitive_complexity)] pub fn get_computed_css_style_string(
1519 &self,
1520 node_data: &NodeData,
1521 node_id: &NodeId,
1522 node_state: &StyledNodeState,
1523 ) -> String {
1524 let mut s = String::new();
1525 if let Some(p) = self.get_background_content(node_data, node_id, node_state) {
1526 let _ = write!(s,"background: {};", p.get_css_value_fmt());
1527 }
1528 if let Some(p) = self.get_background_position(node_data, node_id, node_state) {
1529 let _ = write!(s,"background-position: {};", p.get_css_value_fmt());
1530 }
1531 if let Some(p) = self.get_background_size(node_data, node_id, node_state) {
1532 let _ = write!(s,"background-size: {};", p.get_css_value_fmt());
1533 }
1534 if let Some(p) = self.get_background_repeat(node_data, node_id, node_state) {
1535 let _ = write!(s,"background-repeat: {};", p.get_css_value_fmt());
1536 }
1537 if let Some(p) = self.get_font_size(node_data, node_id, node_state) {
1538 let _ = write!(s,"font-size: {};", p.get_css_value_fmt());
1539 }
1540 if let Some(p) = self.get_font_family(node_data, node_id, node_state) {
1541 let _ = write!(s,"font-family: {};", p.get_css_value_fmt());
1542 }
1543 if let Some(p) = self.get_text_color(node_data, node_id, node_state) {
1544 let _ = write!(s,"color: {};", p.get_css_value_fmt());
1545 }
1546 if let Some(p) = self.get_text_align(node_data, node_id, node_state) {
1547 let _ = write!(s,"text-align: {};", p.get_css_value_fmt());
1548 }
1549 if let Some(p) = self.get_line_height(node_data, node_id, node_state) {
1550 let _ = write!(s,"line-height: {};", p.get_css_value_fmt());
1551 }
1552 if let Some(p) = self.get_letter_spacing(node_data, node_id, node_state) {
1553 let _ = write!(s,"letter-spacing: {};", p.get_css_value_fmt());
1554 }
1555 if let Some(p) = self.get_word_spacing(node_data, node_id, node_state) {
1556 let _ = write!(s,"word-spacing: {};", p.get_css_value_fmt());
1557 }
1558 if let Some(p) = self.get_tab_size(node_data, node_id, node_state) {
1559 let _ = write!(s,"tab-size: {};", p.get_css_value_fmt());
1560 }
1561 if let Some(p) = self.get_cursor(node_data, node_id, node_state) {
1562 let _ = write!(s,"cursor: {};", p.get_css_value_fmt());
1563 }
1564 if let Some(p) = self.get_box_shadow_left(node_data, node_id, node_state) {
1565 let _ = write!(s,
1566 "-azul-box-shadow-left: {};",
1567 p.get_css_value_fmt()
1568 );
1569 }
1570 if let Some(p) = self.get_box_shadow_right(node_data, node_id, node_state) {
1571 let _ = write!(s,
1572 "-azul-box-shadow-right: {};",
1573 p.get_css_value_fmt()
1574 );
1575 }
1576 if let Some(p) = self.get_box_shadow_top(node_data, node_id, node_state) {
1577 let _ = write!(s,"-azul-box-shadow-top: {};", p.get_css_value_fmt());
1578 }
1579 if let Some(p) = self.get_box_shadow_bottom(node_data, node_id, node_state) {
1580 let _ = write!(s,
1581 "-azul-box-shadow-bottom: {};",
1582 p.get_css_value_fmt()
1583 );
1584 }
1585 if let Some(p) = self.get_border_top_color(node_data, node_id, node_state) {
1586 let _ = write!(s,"border-top-color: {};", p.get_css_value_fmt());
1587 }
1588 if let Some(p) = self.get_border_left_color(node_data, node_id, node_state) {
1589 let _ = write!(s,"border-left-color: {};", p.get_css_value_fmt());
1590 }
1591 if let Some(p) = self.get_border_right_color(node_data, node_id, node_state) {
1592 let _ = write!(s,"border-right-color: {};", p.get_css_value_fmt());
1593 }
1594 if let Some(p) = self.get_border_bottom_color(node_data, node_id, node_state) {
1595 let _ = write!(s,"border-bottom-color: {};", p.get_css_value_fmt());
1596 }
1597 if let Some(p) = self.get_border_top_style(node_data, node_id, node_state) {
1598 let _ = write!(s,"border-top-style: {};", p.get_css_value_fmt());
1599 }
1600 if let Some(p) = self.get_border_left_style(node_data, node_id, node_state) {
1601 let _ = write!(s,"border-left-style: {};", p.get_css_value_fmt());
1602 }
1603 if let Some(p) = self.get_border_right_style(node_data, node_id, node_state) {
1604 let _ = write!(s,"border-right-style: {};", p.get_css_value_fmt());
1605 }
1606 if let Some(p) = self.get_border_bottom_style(node_data, node_id, node_state) {
1607 let _ = write!(s,"border-bottom-style: {};", p.get_css_value_fmt());
1608 }
1609 if let Some(p) = self.get_border_top_left_radius(node_data, node_id, node_state) {
1610 let _ = write!(s,
1611 "border-top-left-radius: {};",
1612 p.get_css_value_fmt()
1613 );
1614 }
1615 if let Some(p) = self.get_border_top_right_radius(node_data, node_id, node_state) {
1616 let _ = write!(s,
1617 "border-top-right-radius: {};",
1618 p.get_css_value_fmt()
1619 );
1620 }
1621 if let Some(p) = self.get_border_bottom_left_radius(node_data, node_id, node_state) {
1622 let _ = write!(s,
1623 "border-bottom-left-radius: {};",
1624 p.get_css_value_fmt()
1625 );
1626 }
1627 if let Some(p) = self.get_border_bottom_right_radius(node_data, node_id, node_state) {
1628 let _ = write!(s,
1629 "border-bottom-right-radius: {};",
1630 p.get_css_value_fmt()
1631 );
1632 }
1633 if let Some(p) = self.get_opacity(node_data, node_id, node_state) {
1634 let _ = write!(s,"opacity: {};", p.get_css_value_fmt());
1635 }
1636 if let Some(p) = self.get_transform(node_data, node_id, node_state) {
1637 let _ = write!(s,"transform: {};", p.get_css_value_fmt());
1638 }
1639 if let Some(p) = self.get_transform_origin(node_data, node_id, node_state) {
1640 let _ = write!(s,"transform-origin: {};", p.get_css_value_fmt());
1641 }
1642 if let Some(p) = self.get_perspective_origin(node_data, node_id, node_state) {
1643 let _ = write!(s,"perspective-origin: {};", p.get_css_value_fmt());
1644 }
1645 if let Some(p) = self.get_backface_visibility(node_data, node_id, node_state) {
1646 let _ = write!(s,"backface-visibility: {};", p.get_css_value_fmt());
1647 }
1648 if let Some(p) = self.get_hyphens(node_data, node_id, node_state) {
1649 let _ = write!(s,"hyphens: {};", p.get_css_value_fmt());
1650 }
1651 if let Some(p) = self.get_direction(node_data, node_id, node_state) {
1652 let _ = write!(s,"direction: {};", p.get_css_value_fmt());
1653 }
1654 if let Some(p) = self.get_unicode_bidi(node_data, node_id, node_state) {
1655 let _ = write!(s,"unicode-bidi: {};", p.get_css_value_fmt());
1656 }
1657 if let Some(p) = self.get_text_box_trim(node_data, node_id, node_state) {
1658 let _ = write!(s,"text-box-trim: {};", p.get_css_value_fmt());
1659 }
1660 if let Some(p) = self.get_text_box_edge(node_data, node_id, node_state) {
1661 let _ = write!(s,"text-box-edge: {};", p.get_css_value_fmt());
1662 }
1663 if let Some(p) = self.get_dominant_baseline(node_data, node_id, node_state) {
1664 let _ = write!(s,"dominant-baseline: {};", p.get_css_value_fmt());
1665 }
1666 if let Some(p) = self.get_alignment_baseline(node_data, node_id, node_state) {
1667 let _ = write!(s,"alignment-baseline: {};", p.get_css_value_fmt());
1668 }
1669 if let Some(p) = self.get_baseline_source(node_data, node_id, node_state) {
1670 let _ = write!(s,"baseline-source: {};", p.get_css_value_fmt());
1671 }
1672 if let Some(p) = self.get_line_fit_edge(node_data, node_id, node_state) {
1673 let _ = write!(s,"line-fit-edge: {};", p.get_css_value_fmt());
1674 }
1675 if let Some(p) = self.get_initial_letter_align(node_data, node_id, node_state) {
1676 let _ = write!(s,"initial-letter-align: {};", p.get_css_value_fmt());
1677 }
1678 if let Some(p) = self.get_initial_letter_wrap(node_data, node_id, node_state) {
1679 let _ = write!(s,"initial-letter-wrap: {};", p.get_css_value_fmt());
1680 }
1681 if let Some(p) = self.get_scrollbar_gutter(node_data, node_id, node_state) {
1682 let _ = write!(s,"scrollbar-gutter: {};", p.get_css_value_fmt());
1683 }
1684 if let Some(p) = self.get_overflow_clip_margin(node_data, node_id, node_state) {
1685 let _ = write!(s,"overflow-clip-margin: {};", p.get_css_value_fmt());
1686 }
1687 if let Some(p) = self.get_clip(node_data, node_id, node_state) {
1688 let _ = write!(s,"clip: {};", p.get_css_value_fmt());
1689 }
1690 if let Some(p) = self.get_white_space(node_data, node_id, node_state) {
1691 let _ = write!(s,"white-space: {};", p.get_css_value_fmt());
1692 }
1693 if let Some(p) = self.get_display(node_data, node_id, node_state) {
1694 let _ = write!(s,"display: {};", p.get_css_value_fmt());
1695 }
1696 if let Some(p) = self.get_float(node_data, node_id, node_state) {
1697 let _ = write!(s,"float: {};", p.get_css_value_fmt());
1698 }
1699 if let Some(p) = self.get_box_sizing(node_data, node_id, node_state) {
1700 let _ = write!(s,"box-sizing: {};", p.get_css_value_fmt());
1701 }
1702 if let Some(p) = self.get_width(node_data, node_id, node_state) {
1703 let _ = write!(s,"width: {};", p.get_css_value_fmt());
1704 }
1705 if let Some(p) = self.get_height(node_data, node_id, node_state) {
1706 let _ = write!(s,"height: {};", p.get_css_value_fmt());
1707 }
1708 if let Some(p) = self.get_min_width(node_data, node_id, node_state) {
1709 let _ = write!(s,"min-width: {};", p.get_css_value_fmt());
1710 }
1711 if let Some(p) = self.get_min_height(node_data, node_id, node_state) {
1712 let _ = write!(s,"min-height: {};", p.get_css_value_fmt());
1713 }
1714 if let Some(p) = self.get_max_width(node_data, node_id, node_state) {
1715 let _ = write!(s,"max-width: {};", p.get_css_value_fmt());
1716 }
1717 if let Some(p) = self.get_max_height(node_data, node_id, node_state) {
1718 let _ = write!(s,"max-height: {};", p.get_css_value_fmt());
1719 }
1720 if let Some(p) = self.get_position(node_data, node_id, node_state) {
1721 let _ = write!(s,"position: {};", p.get_css_value_fmt());
1722 }
1723 if let Some(p) = self.get_top(node_data, node_id, node_state) {
1724 let _ = write!(s,"top: {};", p.get_css_value_fmt());
1725 }
1726 if let Some(p) = self.get_bottom(node_data, node_id, node_state) {
1727 let _ = write!(s,"bottom: {};", p.get_css_value_fmt());
1728 }
1729 if let Some(p) = self.get_right(node_data, node_id, node_state) {
1730 let _ = write!(s,"right: {};", p.get_css_value_fmt());
1731 }
1732 if let Some(p) = self.get_left(node_data, node_id, node_state) {
1733 let _ = write!(s,"left: {};", p.get_css_value_fmt());
1734 }
1735 if let Some(p) = self.get_padding_top(node_data, node_id, node_state) {
1736 let _ = write!(s,"padding-top: {};", p.get_css_value_fmt());
1737 }
1738 if let Some(p) = self.get_padding_bottom(node_data, node_id, node_state) {
1739 let _ = write!(s,"padding-bottom: {};", p.get_css_value_fmt());
1740 }
1741 if let Some(p) = self.get_padding_left(node_data, node_id, node_state) {
1742 let _ = write!(s,"padding-left: {};", p.get_css_value_fmt());
1743 }
1744 if let Some(p) = self.get_padding_right(node_data, node_id, node_state) {
1745 let _ = write!(s,"padding-right: {};", p.get_css_value_fmt());
1746 }
1747 if let Some(p) = self.get_margin_top(node_data, node_id, node_state) {
1748 let _ = write!(s,"margin-top: {};", p.get_css_value_fmt());
1749 }
1750 if let Some(p) = self.get_margin_bottom(node_data, node_id, node_state) {
1751 let _ = write!(s,"margin-bottom: {};", p.get_css_value_fmt());
1752 }
1753 if let Some(p) = self.get_margin_left(node_data, node_id, node_state) {
1754 let _ = write!(s,"margin-left: {};", p.get_css_value_fmt());
1755 }
1756 if let Some(p) = self.get_margin_right(node_data, node_id, node_state) {
1757 let _ = write!(s,"margin-right: {};", p.get_css_value_fmt());
1758 }
1759 if let Some(p) = self.get_border_top_width(node_data, node_id, node_state) {
1760 let _ = write!(s,"border-top-width: {};", p.get_css_value_fmt());
1761 }
1762 if let Some(p) = self.get_border_left_width(node_data, node_id, node_state) {
1763 let _ = write!(s,"border-left-width: {};", p.get_css_value_fmt());
1764 }
1765 if let Some(p) = self.get_border_right_width(node_data, node_id, node_state) {
1766 let _ = write!(s,"border-right-width: {};", p.get_css_value_fmt());
1767 }
1768 if let Some(p) = self.get_border_bottom_width(node_data, node_id, node_state) {
1769 let _ = write!(s,"border-bottom-width: {};", p.get_css_value_fmt());
1770 }
1771 if let Some(p) = self.get_overflow_x(node_data, node_id, node_state) {
1772 let _ = write!(s,"overflow-x: {};", p.get_css_value_fmt());
1773 }
1774 if let Some(p) = self.get_overflow_y(node_data, node_id, node_state) {
1775 let _ = write!(s,"overflow-y: {};", p.get_css_value_fmt());
1776 }
1777 if let Some(p) = self.get_flex_direction(node_data, node_id, node_state) {
1778 let _ = write!(s,"flex-direction: {};", p.get_css_value_fmt());
1779 }
1780 if let Some(p) = self.get_flex_wrap(node_data, node_id, node_state) {
1781 let _ = write!(s,"flex-wrap: {};", p.get_css_value_fmt());
1782 }
1783 if let Some(p) = self.get_flex_grow(node_data, node_id, node_state) {
1784 let _ = write!(s,"flex-grow: {};", p.get_css_value_fmt());
1785 }
1786 if let Some(p) = self.get_flex_shrink(node_data, node_id, node_state) {
1787 let _ = write!(s,"flex-shrink: {};", p.get_css_value_fmt());
1788 }
1789 if let Some(p) = self.get_justify_content(node_data, node_id, node_state) {
1790 let _ = write!(s,"justify-content: {};", p.get_css_value_fmt());
1791 }
1792 if let Some(p) = self.get_align_items(node_data, node_id, node_state) {
1793 let _ = write!(s,"align-items: {};", p.get_css_value_fmt());
1794 }
1795 if let Some(p) = self.get_align_content(node_data, node_id, node_state) {
1796 let _ = write!(s,"align-content: {};", p.get_css_value_fmt());
1797 }
1798 s
1799 }
1800}
1801
1802#[repr(C)]
1803#[derive(Debug, PartialEq, Clone)]
1804pub struct CssPropertyCachePtr {
1805 pub ptr: ManuallyDrop<Box<CssPropertyCache>>,
1813 pub run_destructor: bool,
1814}
1815
1816impl CssPropertyCachePtr {
1817 pub fn new(cache: CssPropertyCache) -> Self {
1818 Self {
1819 ptr: ManuallyDrop::new(Box::new(cache)),
1820 run_destructor: true,
1821 }
1822 }
1823 pub fn downcast_mut(&mut self) -> &mut CssPropertyCache {
1824 &mut self.ptr
1825 }
1826}
1827
1828impl Drop for CssPropertyCachePtr {
1829 fn drop(&mut self) {
1830 if self.run_destructor {
1833 self.run_destructor = false;
1834 unsafe {
1835 ManuallyDrop::drop(&mut self.ptr);
1836 }
1837 }
1838 }
1839}
1840
1841macro_rules! impl_get_prop {
1845 ($name:ident, $value_ty:ty, $variant:ident, $as_method:ident) => {
1846 pub fn $name<'a>(
1847 &'a self,
1848 node_data: &'a NodeData,
1849 node_id: &NodeId,
1850 node_state: &StyledNodeState,
1851 ) -> Option<&'a $value_ty> {
1852 self.get_property(node_data, node_id, node_state, &CssPropertyType::$variant)
1853 .and_then(|p| p.$as_method())
1854 }
1855 };
1856}
1857
1858impl CssPropertyCache {
1859 #[must_use] pub fn empty(node_count: usize) -> Self {
1860 Self {
1861 node_count,
1862 retained_author_css: Css::default(),
1863 user_overridden_properties: Vec::new(),
1864
1865 cascaded_props: FlatVecVec::new(node_count),
1866 css_props: FlatVecVec::new(node_count),
1867
1868 computed_values: Vec::new(),
1869 compact_cache: None,
1870 global_css_props: Vec::new(),
1871 resolved_font_sizes_px: crate::sync::OnceLock::new(),
1872 }
1873 }
1874
1875 pub fn invalidate_resolved_font_sizes(&mut self) {
1881 self.resolved_font_sizes_px = crate::sync::OnceLock::new();
1882 }
1883
1884 pub fn append(&mut self, other: &mut Self) {
1885 self.user_overridden_properties.append(&mut other.user_overridden_properties);
1886 self.cascaded_props.extend_from(&mut other.cascaded_props);
1887 self.css_props.extend_from(&mut other.css_props);
1888 self.computed_values.append(&mut other.computed_values);
1889
1890 self.node_count += other.node_count;
1891 self.resolved_font_sizes_px = crate::sync::OnceLock::new();
1893
1894 self.compact_cache = None;
1896 }
1897
1898 pub fn is_horizontal_overflow_visible(
1899 &self,
1900 node_data: &NodeData,
1901 node_id: &NodeId,
1902 node_state: &StyledNodeState,
1903 ) -> bool {
1904 self.get_overflow_x(node_data, node_id, node_state)
1905 .and_then(|p| p.get_property_or_default())
1906 .unwrap_or_default()
1907 .is_overflow_visible()
1908 }
1909
1910 pub fn is_vertical_overflow_visible(
1911 &self,
1912 node_data: &NodeData,
1913 node_id: &NodeId,
1914 node_state: &StyledNodeState,
1915 ) -> bool {
1916 self.get_overflow_y(node_data, node_id, node_state)
1917 .and_then(|p| p.get_property_or_default())
1918 .unwrap_or_default()
1919 .is_overflow_visible()
1920 }
1921
1922 pub fn is_horizontal_overflow_hidden(
1923 &self,
1924 node_data: &NodeData,
1925 node_id: &NodeId,
1926 node_state: &StyledNodeState,
1927 ) -> bool {
1928 self.get_overflow_x(node_data, node_id, node_state)
1929 .and_then(|p| p.get_property_or_default())
1930 .unwrap_or_default()
1931 .is_overflow_hidden()
1932 }
1933
1934 pub fn is_vertical_overflow_hidden(
1935 &self,
1936 node_data: &NodeData,
1937 node_id: &NodeId,
1938 node_state: &StyledNodeState,
1939 ) -> bool {
1940 self.get_overflow_y(node_data, node_id, node_state)
1941 .and_then(|p| p.get_property_or_default())
1942 .unwrap_or_default()
1943 .is_overflow_hidden()
1944 }
1945
1946 pub fn get_text_color_or_default(
1947 &self,
1948 node_data: &NodeData,
1949 node_id: &NodeId,
1950 node_state: &StyledNodeState,
1951 ) -> StyleTextColor {
1952 use azul_css::defaults::DEFAULT_TEXT_COLOR;
1953 self.get_text_color(node_data, node_id, node_state)
1954 .and_then(|fs| fs.get_property().copied())
1955 .unwrap_or(DEFAULT_TEXT_COLOR)
1956 }
1957
1958 pub fn get_font_id_or_default(
1960 &self,
1961 node_data: &NodeData,
1962 node_id: &NodeId,
1963 node_state: &StyledNodeState,
1964 ) -> StyleFontFamilyVec {
1965 use azul_css::defaults::DEFAULT_FONT_ID;
1966 let default_font_id = vec![StyleFontFamily::System(AzString::from_const_str(
1967 DEFAULT_FONT_ID,
1968 ))]
1969 .into();
1970 let font_family_opt = self.get_font_family(node_data, node_id, node_state);
1971
1972 font_family_opt
1973 .as_ref()
1974 .and_then(|family| Some(family.get_property()?.clone()))
1975 .unwrap_or(default_font_id)
1976 }
1977
1978 pub fn get_font_size_or_default(
1979 &self,
1980 node_data: &NodeData,
1981 node_id: &NodeId,
1982 node_state: &StyledNodeState,
1983 ) -> StyleFontSize {
1984 use azul_css::defaults::DEFAULT_FONT_SIZE;
1985 self.get_font_size(node_data, node_id, node_state)
1986 .and_then(|fs| fs.get_property().copied())
1987 .unwrap_or(DEFAULT_FONT_SIZE)
1988 }
1989
1990 pub fn has_border(
1991 &self,
1992 node_data: &NodeData,
1993 node_id: &NodeId,
1994 node_state: &StyledNodeState,
1995 ) -> bool {
1996 self.get_border_left_width(node_data, node_id, node_state)
1997 .is_some()
1998 || self
1999 .get_border_right_width(node_data, node_id, node_state)
2000 .is_some()
2001 || self
2002 .get_border_top_width(node_data, node_id, node_state)
2003 .is_some()
2004 || self
2005 .get_border_bottom_width(node_data, node_id, node_state)
2006 .is_some()
2007 }
2008
2009 pub fn has_box_shadow(
2010 &self,
2011 node_data: &NodeData,
2012 node_id: &NodeId,
2013 node_state: &StyledNodeState,
2014 ) -> bool {
2015 self.get_box_shadow_left(node_data, node_id, node_state)
2016 .is_some()
2017 || self
2018 .get_box_shadow_right(node_data, node_id, node_state)
2019 .is_some()
2020 || self
2021 .get_box_shadow_top(node_data, node_id, node_state)
2022 .is_some()
2023 || self
2024 .get_box_shadow_bottom(node_data, node_id, node_state)
2025 .is_some()
2026 }
2027
2028 pub fn get_property<'a>(
2029 &'a self,
2030 node_data: &'a NodeData,
2031 node_id: &NodeId,
2032 node_state: &StyledNodeState,
2033 css_property_type: &CssPropertyType,
2034 ) -> Option<&'a CssProperty> {
2035 #[cfg(feature = "std")]
2052 {
2053 static PROP_COUNT_ENABLED: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2054 let enabled = *PROP_COUNT_ENABLED.get_or_init(crate::profile::cascade_enabled);
2055 if enabled {
2056 let _ = PROP_COUNTS.try_with(|c| {
2063 *c.borrow_mut()
2064 .entry(Self::css_prop_type_label(css_property_type))
2065 .or_insert(0) += 1;
2066 });
2067 }
2068 }
2069
2070 self.get_property_slow(node_data, node_id, node_state, css_property_type)
2074 }
2075
2076 #[cfg(feature = "std")]
2077 #[allow(clippy::trivially_copy_pass_by_ref)] fn css_prop_type_label(t: &CssPropertyType) -> &'static str {
2079 use std::sync::{Mutex, OnceLock};
2084 static TABLE: OnceLock<Mutex<std::collections::HashMap<CssPropertyType, &'static str>>> =
2085 OnceLock::new();
2086 let m = TABLE.get_or_init(|| Mutex::new(std::collections::HashMap::new()));
2087 let mut g = m.lock().expect("AZ_PROP_COUNT label table poisoned");
2088 if let Some(s) = g.get(t) {
2089 return s;
2090 }
2091 let s: String = std::format!("{t:?}");
2092 let leaked: &'static str = std::boxed::Box::leak(s.into_boxed_str());
2093 g.insert(*t, leaked);
2094 leaked
2095 }
2096
2097 #[allow(clippy::trivially_copy_pass_by_ref)] #[allow(clippy::too_many_lines)] pub(crate) fn get_property_slow<'a>(
2103 &'a self,
2104 node_data: &'a NodeData,
2105 node_id: &NodeId,
2106 node_state: &StyledNodeState,
2107 css_property_type: &CssPropertyType,
2108 ) -> Option<&'a CssProperty> {
2109
2110 use azul_css::dynamic_selector::{DynamicSelector, PseudoStateType};
2111
2112 fn matches_pseudo_state(
2116 conds: &azul_css::dynamic_selector::DynamicSelectorVec,
2117 state: PseudoStateType,
2118 ) -> bool {
2119 let conditions = conds.as_slice();
2120 if conditions.is_empty() {
2121 state == PseudoStateType::Normal
2122 } else {
2123 conditions
2124 .iter()
2125 .all(|c| matches!(c, DynamicSelector::PseudoState(s) if *s == state))
2126 }
2127 }
2128
2129 if let Some(v) = self.user_overridden_properties.get(node_id.index()) {
2131 if let Ok(idx) = v.binary_search_by_key(css_property_type, |(k, _)| *k) {
2132 return Some(&v[idx].1);
2133 }
2134 }
2135
2136 if node_state.focused {
2139 if let Some(p) = node_data.style.iter_inline_properties().find_map(|(prop, conds)| {
2141 if matches_pseudo_state(conds,PseudoStateType::Focus)
2142 && prop.get_type() == *css_property_type
2143 {
2144 Some(prop)
2145 } else {
2146 None
2147 }
2148 }) {
2149 return Some(p);
2150 }
2151
2152 if let Some(p) = Self::find_in_stateful(
2154 self.css_props.get_slice(node_id.index()),
2155 PseudoStateType::Focus,
2156 css_property_type,
2157 ) {
2158 return Some(p);
2159 }
2160
2161 if let Some(p) = Self::find_in_stateful(
2163 self.cascaded_props.get_slice(node_id.index()),
2164 PseudoStateType::Focus,
2165 css_property_type,
2166 ) {
2167 return Some(p);
2168 }
2169 }
2170
2171 if node_state.active {
2172 if let Some(p) = node_data.style.iter_inline_properties().find_map(|(prop, conds)| {
2174 if matches_pseudo_state(conds,PseudoStateType::Active)
2175 && prop.get_type() == *css_property_type
2176 {
2177 Some(prop)
2178 } else {
2179 None
2180 }
2181 }) {
2182 return Some(p);
2183 }
2184
2185 if let Some(p) = Self::find_in_stateful(
2187 self.css_props.get_slice(node_id.index()),
2188 PseudoStateType::Active,
2189 css_property_type,
2190 ) {
2191 return Some(p);
2192 }
2193
2194 if let Some(p) = Self::find_in_stateful(
2196 self.cascaded_props.get_slice(node_id.index()),
2197 PseudoStateType::Active,
2198 css_property_type,
2199 ) {
2200 return Some(p);
2201 }
2202 }
2203
2204 if node_state.dragging {
2206 if let Some(p) = node_data.style.iter_inline_properties().find_map(|(prop, conds)| {
2207 if matches_pseudo_state(conds,PseudoStateType::Dragging)
2208 && prop.get_type() == *css_property_type
2209 {
2210 Some(prop)
2211 } else {
2212 None
2213 }
2214 }) {
2215 return Some(p);
2216 }
2217
2218 if let Some(p) = Self::find_in_stateful(
2219 self.css_props.get_slice(node_id.index()),
2220 PseudoStateType::Dragging,
2221 css_property_type,
2222 ) {
2223 return Some(p);
2224 }
2225
2226 if let Some(p) = Self::find_in_stateful(
2227 self.cascaded_props.get_slice(node_id.index()),
2228 PseudoStateType::Dragging,
2229 css_property_type,
2230 ) {
2231 return Some(p);
2232 }
2233 }
2234
2235 if node_state.drag_over {
2237 if let Some(p) = node_data.style.iter_inline_properties().find_map(|(prop, conds)| {
2238 if matches_pseudo_state(conds,PseudoStateType::DragOver)
2239 && prop.get_type() == *css_property_type
2240 {
2241 Some(prop)
2242 } else {
2243 None
2244 }
2245 }) {
2246 return Some(p);
2247 }
2248
2249 if let Some(p) = Self::find_in_stateful(
2250 self.css_props.get_slice(node_id.index()),
2251 PseudoStateType::DragOver,
2252 css_property_type,
2253 ) {
2254 return Some(p);
2255 }
2256
2257 if let Some(p) = Self::find_in_stateful(
2258 self.cascaded_props.get_slice(node_id.index()),
2259 PseudoStateType::DragOver,
2260 css_property_type,
2261 ) {
2262 return Some(p);
2263 }
2264 }
2265
2266 if node_state.hover {
2267 if let Some(p) = node_data.style.iter_inline_properties().find_map(|(prop, conds)| {
2269 if matches_pseudo_state(conds,PseudoStateType::Hover)
2270 && prop.get_type() == *css_property_type
2271 {
2272 Some(prop)
2273 } else {
2274 None
2275 }
2276 }) {
2277 return Some(p);
2278 }
2279
2280 if let Some(p) = Self::find_in_stateful(
2282 self.css_props.get_slice(node_id.index()),
2283 PseudoStateType::Hover,
2284 css_property_type,
2285 ) {
2286 return Some(p);
2287 }
2288
2289 if let Some(p) = Self::find_in_stateful(
2291 self.cascaded_props.get_slice(node_id.index()),
2292 PseudoStateType::Hover,
2293 css_property_type,
2294 ) {
2295 return Some(p);
2296 }
2297 }
2298
2299 if let Some(p) = node_data.style.iter_inline_properties().find_map(|(prop, conds)| {
2302 if matches_pseudo_state(conds, PseudoStateType::Normal)
2303 && prop.get_type() == *css_property_type
2304 {
2305 Some(prop)
2306 } else {
2307 None
2308 }
2309 }) {
2310 return Some(p);
2311 }
2312
2313 if let Some(p) = Self::find_in_stateful(
2315 self.css_props.get_slice(node_id.index()),
2316 PseudoStateType::Normal,
2317 css_property_type,
2318 ) {
2319 return Some(p);
2320 }
2321
2322 if let Some(p) = self.global_css_props.iter().find(|p| p.get_type() == *css_property_type) {
2326 return Some(p);
2327 }
2328
2329 if let Some(p) = Self::find_in_stateful(
2331 self.cascaded_props.get_slice(node_id.index()),
2332 PseudoStateType::Normal,
2333 css_property_type,
2334 ) {
2335 return Some(p);
2336 }
2337
2338 if css_property_type.is_inheritable() {
2341 if let Some(vec) = self.computed_values.get(node_id.index()) {
2342 if let Ok(idx) = vec.binary_search_by_key(css_property_type, |(k, _)| *k) {
2343 return Some(&vec[idx].1.property);
2344 }
2345 }
2346 }
2347
2348 crate::ua_css::get_ua_property(&node_data.node_type, *css_property_type)
2351 }
2352
2353 #[allow(clippy::trivially_copy_pass_by_ref)] pub(crate) fn get_property_with_context<'a>(
2362 &'a self,
2363 node_data: &'a NodeData,
2364 node_id: &NodeId,
2365 context: &DynamicSelectorContext,
2366 css_property_type: &CssPropertyType,
2367 ) -> Option<&'a CssProperty> {
2368 if let Some(v) = self.user_overridden_properties.get(node_id.index()) {
2370 if let Ok(idx) = v.binary_search_by_key(css_property_type, |(k, _)| *k) {
2371 return Some(&v[idx].1);
2372 }
2373 }
2374
2375 let mut last_inline = None;
2383 for (prop, conds) in node_data.style.iter_inline_properties() {
2384 let conditions_match = conds.as_slice().iter().all(|c| c.matches(context));
2385 if prop.get_type() == *css_property_type && conditions_match {
2386 last_inline = Some(prop);
2387 }
2388 }
2389 if let Some(prop) = last_inline {
2390 return Some(prop);
2391 }
2392
2393 let legacy_state = StyledNodeState::from_pseudo_state_flags(&context.pseudo_state);
2395 if let Some(p) = self.get_property(node_data, node_id, &legacy_state, css_property_type) {
2396 return Some(p);
2397 }
2398
2399 None
2400 }
2401
2402 pub(crate) fn check_properties_changed(
2405 node_data: &NodeData,
2406 old_context: &DynamicSelectorContext,
2407 new_context: &DynamicSelectorContext,
2408 ) -> bool {
2409 for (_prop, conds) in node_data.style.iter_inline_properties() {
2410 let was_active = conds.as_slice().iter().all(|c| c.matches(old_context));
2411 let is_active = conds.as_slice().iter().all(|c| c.matches(new_context));
2412 if was_active != is_active {
2413 return true;
2414 }
2415 }
2416 false
2417 }
2418
2419 pub(crate) fn check_layout_properties_changed(
2422 node_data: &NodeData,
2423 old_context: &DynamicSelectorContext,
2424 new_context: &DynamicSelectorContext,
2425 ) -> bool {
2426 for (prop, conds) in node_data.style.iter_inline_properties() {
2427 if !prop.get_type().can_trigger_relayout() {
2429 continue;
2430 }
2431
2432 let was_active = conds.as_slice().iter().all(|c| c.matches(old_context));
2433 let is_active = conds.as_slice().iter().all(|c| c.matches(new_context));
2434 if was_active != is_active {
2435 return true;
2436 }
2437 }
2438 false
2439 }
2440
2441 impl_get_prop!(get_background_content, StyleBackgroundContentVecValue, BackgroundContent, as_background_content);
2442
2443 impl_get_prop!(get_hyphens, StyleHyphensValue, Hyphens, as_hyphens);
2444
2445 impl_get_prop!(get_word_break, StyleWordBreakValue, WordBreak, as_word_break);
2446
2447 impl_get_prop!(get_overflow_wrap, StyleOverflowWrapValue, OverflowWrap, as_overflow_wrap);
2448
2449 impl_get_prop!(get_line_break, StyleLineBreakValue, LineBreak, as_line_break);
2450
2451 impl_get_prop!(get_text_align_last, StyleTextAlignLastValue, TextAlignLast, as_text_align_last);
2452
2453 impl_get_prop!(get_text_transform, StyleTextTransformValue, TextTransform, as_text_transform);
2454
2455 impl_get_prop!(get_object_fit, StyleObjectFitValue, ObjectFit, as_object_fit);
2456
2457 impl_get_prop!(get_text_overflow, StyleTextOverflowValue, TextOverflow, as_text_overflow);
2458
2459 impl_get_prop!(get_text_orientation, StyleTextOrientationValue, TextOrientation, as_text_orientation);
2460
2461 impl_get_prop!(get_object_position, StyleObjectPositionValue, ObjectPosition, as_object_position);
2462
2463 impl_get_prop!(get_aspect_ratio, StyleAspectRatioValue, AspectRatio, as_aspect_ratio);
2464
2465 impl_get_prop!(get_direction, StyleDirectionValue, Direction, as_direction);
2466
2467 impl_get_prop!(get_unicode_bidi, StyleUnicodeBidiValue, UnicodeBidi, as_unicode_bidi);
2468
2469 impl_get_prop!(get_text_box_trim, StyleTextBoxTrimValue, TextBoxTrim, as_text_box_trim);
2470
2471 impl_get_prop!(get_text_box_edge, StyleTextBoxEdgeValue, TextBoxEdge, as_text_box_edge);
2472
2473 impl_get_prop!(get_dominant_baseline, StyleDominantBaselineValue, DominantBaseline, as_dominant_baseline);
2474
2475 impl_get_prop!(get_alignment_baseline, StyleAlignmentBaselineValue, AlignmentBaseline, as_alignment_baseline);
2476
2477 impl_get_prop!(get_baseline_source, StyleBaselineSourceValue, BaselineSource, as_baseline_source);
2478
2479 impl_get_prop!(get_line_fit_edge, StyleLineFitEdgeValue, LineFitEdge, as_line_fit_edge);
2480
2481 impl_get_prop!(get_initial_letter_align, StyleInitialLetterAlignValue, InitialLetterAlign, as_initial_letter_align);
2482
2483 impl_get_prop!(get_initial_letter_wrap, StyleInitialLetterWrapValue, InitialLetterWrap, as_initial_letter_wrap);
2484
2485 impl_get_prop!(get_scrollbar_gutter, StyleScrollbarGutterValue, ScrollbarGutter, as_scrollbar_gutter);
2486
2487 impl_get_prop!(get_overflow_clip_margin, StyleOverflowClipMarginValue, OverflowClipMargin, as_overflow_clip_margin);
2488
2489 impl_get_prop!(get_clip, StyleClipRectValue, Clip, as_clip);
2490
2491 impl_get_prop!(get_white_space, StyleWhiteSpaceValue, WhiteSpace, as_white_space);
2492 impl_get_prop!(get_background_position, StyleBackgroundPositionVecValue, BackgroundPosition, as_background_position);
2493 impl_get_prop!(get_background_size, StyleBackgroundSizeVecValue, BackgroundSize, as_background_size);
2494 impl_get_prop!(get_background_repeat, StyleBackgroundRepeatVecValue, BackgroundRepeat, as_background_repeat);
2495 impl_get_prop!(get_font_size, StyleFontSizeValue, FontSize, as_font_size);
2496 impl_get_prop!(get_font_family, StyleFontFamilyVecValue, FontFamily, as_font_family);
2497 impl_get_prop!(get_font_weight, StyleFontWeightValue, FontWeight, as_font_weight);
2498 impl_get_prop!(get_font_style, StyleFontStyleValue, FontStyle, as_font_style);
2499 impl_get_prop!(get_text_color, StyleTextColorValue, TextColor, as_text_color);
2500 impl_get_prop!(get_text_indent, StyleTextIndentValue, TextIndent, as_text_indent);
2501 impl_get_prop!(get_initial_letter, StyleInitialLetterValue, InitialLetter, as_initial_letter);
2502 impl_get_prop!(get_line_clamp, StyleLineClampValue, LineClamp, as_line_clamp);
2503 impl_get_prop!(get_hanging_punctuation, StyleHangingPunctuationValue, HangingPunctuation, as_hanging_punctuation);
2504 impl_get_prop!(get_text_combine_upright, StyleTextCombineUprightValue, TextCombineUpright, as_text_combine_upright);
2505 impl_get_prop!(get_exclusion_margin, StyleExclusionMarginValue, ExclusionMargin, as_exclusion_margin);
2506 impl_get_prop!(get_hyphenation_language, StyleHyphenationLanguageValue, HyphenationLanguage, as_hyphenation_language);
2507 impl_get_prop!(get_caret_color, CaretColorValue, CaretColor, as_caret_color);
2508
2509 impl_get_prop!(get_caret_width, CaretWidthValue, CaretWidth, as_caret_width);
2510
2511 impl_get_prop!(get_caret_animation_duration, CaretAnimationDurationValue, CaretAnimationDuration, as_caret_animation_duration);
2512
2513 impl_get_prop!(get_selection_background_color, SelectionBackgroundColorValue, SelectionBackgroundColor, as_selection_background_color);
2514
2515 impl_get_prop!(get_selection_color, SelectionColorValue, SelectionColor, as_selection_color);
2516
2517 impl_get_prop!(get_selection_radius, SelectionRadiusValue, SelectionRadius, as_selection_radius);
2518
2519 impl_get_prop!(get_text_justify, LayoutTextJustifyValue, TextJustify, as_text_justify);
2520
2521 impl_get_prop!(get_z_index, LayoutZIndexValue, ZIndex, as_z_index);
2522
2523 impl_get_prop!(get_flex_basis, LayoutFlexBasisValue, FlexBasis, as_flex_basis);
2524
2525 impl_get_prop!(get_column_gap, LayoutColumnGapValue, ColumnGap, as_column_gap);
2526
2527 impl_get_prop!(get_row_gap, LayoutRowGapValue, RowGap, as_row_gap);
2528
2529 impl_get_prop!(get_grid_template_columns, LayoutGridTemplateColumnsValue, GridTemplateColumns, as_grid_template_columns);
2530
2531 impl_get_prop!(get_grid_template_rows, LayoutGridTemplateRowsValue, GridTemplateRows, as_grid_template_rows);
2532
2533 impl_get_prop!(get_grid_auto_columns, LayoutGridAutoColumnsValue, GridAutoColumns, as_grid_auto_columns);
2534
2535 impl_get_prop!(get_grid_auto_rows, LayoutGridAutoRowsValue, GridAutoRows, as_grid_auto_rows);
2536
2537 impl_get_prop!(get_grid_column, LayoutGridColumnValue, GridColumn, as_grid_column);
2538
2539 impl_get_prop!(get_grid_row, LayoutGridRowValue, GridRow, as_grid_row);
2540
2541 impl_get_prop!(get_grid_auto_flow, LayoutGridAutoFlowValue, GridAutoFlow, as_grid_auto_flow);
2542
2543 impl_get_prop!(get_justify_self, LayoutJustifySelfValue, JustifySelf, as_justify_self);
2544
2545 impl_get_prop!(get_justify_items, LayoutJustifyItemsValue, JustifyItems, as_justify_items);
2546
2547 impl_get_prop!(get_gap, LayoutGapValue, Gap, as_gap);
2548
2549 #[allow(clippy::trivially_copy_pass_by_ref)] pub(crate) fn get_grid_gap<'a>(
2552 &'a self,
2553 node_data: &'a NodeData,
2554 node_id: &NodeId,
2555 node_state: &StyledNodeState,
2556 ) -> Option<&'a LayoutGapValue> {
2557 self.get_property(node_data, node_id, node_state, &CssPropertyType::GridGap)
2558 .and_then(|p| p.as_grid_gap())
2559 }
2560
2561 impl_get_prop!(get_align_self, LayoutAlignSelfValue, AlignSelf, as_align_self);
2562
2563 impl_get_prop!(get_font, StyleFontValue, Font, as_font);
2564
2565 impl_get_prop!(get_writing_mode, LayoutWritingModeValue, WritingMode, as_writing_mode);
2566
2567 impl_get_prop!(get_clear, LayoutClearValue, Clear, as_clear);
2568
2569 impl_get_prop!(get_shape_outside, ShapeOutsideValue, ShapeOutside, as_shape_outside);
2570
2571 impl_get_prop!(get_shape_inside, ShapeInsideValue, ShapeInside, as_shape_inside);
2572
2573 impl_get_prop!(get_clip_path, ClipPathValue, ClipPath, as_clip_path);
2574
2575 pub fn get_scrollbar_track<'a>(
2577 &'a self,
2578 node_data: &'a NodeData,
2579 node_id: &NodeId,
2580 node_state: &StyledNodeState,
2581 ) -> Option<&'a StyleBackgroundContentValue> {
2582 self.get_property(node_data, node_id, node_state, &CssPropertyType::ScrollbarTrack)
2583 .and_then(|p| p.as_scrollbar_track())
2584 }
2585
2586 pub fn get_scrollbar_thumb<'a>(
2588 &'a self,
2589 node_data: &'a NodeData,
2590 node_id: &NodeId,
2591 node_state: &StyledNodeState,
2592 ) -> Option<&'a StyleBackgroundContentValue> {
2593 self.get_property(node_data, node_id, node_state, &CssPropertyType::ScrollbarThumb)
2594 .and_then(|p| p.as_scrollbar_thumb())
2595 }
2596
2597 pub fn get_scrollbar_button<'a>(
2599 &'a self,
2600 node_data: &'a NodeData,
2601 node_id: &NodeId,
2602 node_state: &StyledNodeState,
2603 ) -> Option<&'a StyleBackgroundContentValue> {
2604 self.get_property(node_data, node_id, node_state, &CssPropertyType::ScrollbarButton)
2605 .and_then(|p| p.as_scrollbar_button())
2606 }
2607
2608 pub fn get_scrollbar_corner<'a>(
2610 &'a self,
2611 node_data: &'a NodeData,
2612 node_id: &NodeId,
2613 node_state: &StyledNodeState,
2614 ) -> Option<&'a StyleBackgroundContentValue> {
2615 self.get_property(node_data, node_id, node_state, &CssPropertyType::ScrollbarCorner)
2616 .and_then(|p| p.as_scrollbar_corner())
2617 }
2618
2619 pub fn get_scrollbar_resizer<'a>(
2621 &'a self,
2622 node_data: &'a NodeData,
2623 node_id: &NodeId,
2624 node_state: &StyledNodeState,
2625 ) -> Option<&'a StyleBackgroundContentValue> {
2626 self.get_property(node_data, node_id, node_state, &CssPropertyType::ScrollbarResizer)
2627 .and_then(|p| p.as_scrollbar_resizer())
2628 }
2629
2630 impl_get_prop!(get_scrollbar_width, LayoutScrollbarWidthValue, ScrollbarWidth, as_scrollbar_width);
2631
2632 impl_get_prop!(get_scrollbar_color, StyleScrollbarColorValue, ScrollbarColor, as_scrollbar_color);
2633
2634 impl_get_prop!(get_scrollbar_visibility, ScrollbarVisibilityModeValue, ScrollbarVisibility, as_scrollbar_visibility);
2635
2636 impl_get_prop!(get_scrollbar_fade_delay, ScrollbarFadeDelayValue, ScrollbarFadeDelay, as_scrollbar_fade_delay);
2637
2638 impl_get_prop!(get_scrollbar_fade_duration, ScrollbarFadeDurationValue, ScrollbarFadeDuration, as_scrollbar_fade_duration);
2639
2640 impl_get_prop!(get_visibility, StyleVisibilityValue, Visibility, as_visibility);
2641
2642 impl_get_prop!(get_break_before, PageBreakValue, BreakBefore, as_break_before);
2643
2644 impl_get_prop!(get_break_after, PageBreakValue, BreakAfter, as_break_after);
2645
2646 impl_get_prop!(get_break_inside, BreakInsideValue, BreakInside, as_break_inside);
2647
2648 impl_get_prop!(get_orphans, OrphansValue, Orphans, as_orphans);
2649
2650 impl_get_prop!(get_widows, WidowsValue, Widows, as_widows);
2651
2652 impl_get_prop!(get_box_decoration_break, BoxDecorationBreakValue, BoxDecorationBreak, as_box_decoration_break);
2653
2654 impl_get_prop!(get_column_count, ColumnCountValue, ColumnCount, as_column_count);
2655
2656 impl_get_prop!(get_column_width, ColumnWidthValue, ColumnWidth, as_column_width);
2657
2658 impl_get_prop!(get_column_span, ColumnSpanValue, ColumnSpan, as_column_span);
2659
2660 impl_get_prop!(get_column_fill, ColumnFillValue, ColumnFill, as_column_fill);
2661
2662 impl_get_prop!(get_column_rule_width, ColumnRuleWidthValue, ColumnRuleWidth, as_column_rule_width);
2663
2664 impl_get_prop!(get_column_rule_style, ColumnRuleStyleValue, ColumnRuleStyle, as_column_rule_style);
2665
2666 impl_get_prop!(get_column_rule_color, ColumnRuleColorValue, ColumnRuleColor, as_column_rule_color);
2667
2668 impl_get_prop!(get_flow_into, FlowIntoValue, FlowInto, as_flow_into);
2669
2670 impl_get_prop!(get_flow_from, FlowFromValue, FlowFrom, as_flow_from);
2671
2672 impl_get_prop!(get_shape_margin, ShapeMarginValue, ShapeMargin, as_shape_margin);
2673
2674 impl_get_prop!(get_shape_image_threshold, ShapeImageThresholdValue, ShapeImageThreshold, as_shape_image_threshold);
2675
2676 impl_get_prop!(get_content, ContentValue, Content, as_content);
2677
2678 impl_get_prop!(get_counter_reset, CounterResetValue, CounterReset, as_counter_reset);
2679
2680 impl_get_prop!(get_counter_increment, CounterIncrementValue, CounterIncrement, as_counter_increment);
2681
2682 impl_get_prop!(get_string_set, StringSetValue, StringSet, as_string_set);
2683 impl_get_prop!(get_text_align, StyleTextAlignValue, TextAlign, as_text_align);
2684 impl_get_prop!(get_user_select, StyleUserSelectValue, UserSelect, as_user_select);
2685 impl_get_prop!(get_text_decoration, StyleTextDecorationValue, TextDecoration, as_text_decoration);
2686 impl_get_prop!(get_vertical_align, StyleVerticalAlignValue, VerticalAlign, as_vertical_align);
2687 impl_get_prop!(get_line_height, StyleLineHeightValue, LineHeight, as_line_height);
2688 impl_get_prop!(get_letter_spacing, StyleLetterSpacingValue, LetterSpacing, as_letter_spacing);
2689 impl_get_prop!(get_word_spacing, StyleWordSpacingValue, WordSpacing, as_word_spacing);
2690 impl_get_prop!(get_tab_size, StyleTabSizeValue, TabSize, as_tab_size);
2691 impl_get_prop!(get_cursor, StyleCursorValue, Cursor, as_cursor);
2692 impl_get_prop!(get_box_shadow_left, StyleBoxShadowValue, BoxShadowLeft, as_box_shadow_left);
2693 impl_get_prop!(get_box_shadow_right, StyleBoxShadowValue, BoxShadowRight, as_box_shadow_right);
2694 impl_get_prop!(get_box_shadow_top, StyleBoxShadowValue, BoxShadowTop, as_box_shadow_top);
2695 impl_get_prop!(get_box_shadow_bottom, StyleBoxShadowValue, BoxShadowBottom, as_box_shadow_bottom);
2696 impl_get_prop!(get_border_top_color, StyleBorderTopColorValue, BorderTopColor, as_border_top_color);
2697 impl_get_prop!(get_border_left_color, StyleBorderLeftColorValue, BorderLeftColor, as_border_left_color);
2698 impl_get_prop!(get_border_right_color, StyleBorderRightColorValue, BorderRightColor, as_border_right_color);
2699 impl_get_prop!(get_border_bottom_color, StyleBorderBottomColorValue, BorderBottomColor, as_border_bottom_color);
2700 impl_get_prop!(get_border_top_style, StyleBorderTopStyleValue, BorderTopStyle, as_border_top_style);
2701 impl_get_prop!(get_border_left_style, StyleBorderLeftStyleValue, BorderLeftStyle, as_border_left_style);
2702 impl_get_prop!(get_border_right_style, StyleBorderRightStyleValue, BorderRightStyle, as_border_right_style);
2703 impl_get_prop!(get_border_bottom_style, StyleBorderBottomStyleValue, BorderBottomStyle, as_border_bottom_style);
2704 impl_get_prop!(get_border_top_left_radius, StyleBorderTopLeftRadiusValue, BorderTopLeftRadius, as_border_top_left_radius);
2705 impl_get_prop!(get_border_top_right_radius, StyleBorderTopRightRadiusValue, BorderTopRightRadius, as_border_top_right_radius);
2706 impl_get_prop!(get_border_bottom_left_radius, StyleBorderBottomLeftRadiusValue, BorderBottomLeftRadius, as_border_bottom_left_radius);
2707 impl_get_prop!(get_border_bottom_right_radius, StyleBorderBottomRightRadiusValue, BorderBottomRightRadius, as_border_bottom_right_radius);
2708 impl_get_prop!(get_opacity, StyleOpacityValue, Opacity, as_opacity);
2709 impl_get_prop!(get_transform, StyleTransformVecValue, Transform, as_transform);
2710 impl_get_prop!(get_transform_origin, StyleTransformOriginValue, TransformOrigin, as_transform_origin);
2711 impl_get_prop!(get_perspective_origin, StylePerspectiveOriginValue, PerspectiveOrigin, as_perspective_origin);
2712 impl_get_prop!(get_backface_visibility, StyleBackfaceVisibilityValue, BackfaceVisibility, as_backface_visibility);
2713 impl_get_prop!(get_display, LayoutDisplayValue, Display, as_display);
2714 impl_get_prop!(get_float, LayoutFloatValue, Float, as_float);
2715 impl_get_prop!(get_box_sizing, LayoutBoxSizingValue, BoxSizing, as_box_sizing);
2716 impl_get_prop!(get_width, LayoutWidthValue, Width, as_width);
2717 impl_get_prop!(get_height, LayoutHeightValue, Height, as_height);
2718 impl_get_prop!(get_min_width, LayoutMinWidthValue, MinWidth, as_min_width);
2719 impl_get_prop!(get_min_height, LayoutMinHeightValue, MinHeight, as_min_height);
2720 impl_get_prop!(get_max_width, LayoutMaxWidthValue, MaxWidth, as_max_width);
2721 impl_get_prop!(get_max_height, LayoutMaxHeightValue, MaxHeight, as_max_height);
2722 impl_get_prop!(get_position, LayoutPositionValue, Position, as_position);
2723 impl_get_prop!(get_top, LayoutTopValue, Top, as_top);
2724 impl_get_prop!(get_bottom, LayoutInsetBottomValue, Bottom, as_bottom);
2725 impl_get_prop!(get_right, LayoutRightValue, Right, as_right);
2726 impl_get_prop!(get_left, LayoutLeftValue, Left, as_left);
2727 impl_get_prop!(get_padding_top, LayoutPaddingTopValue, PaddingTop, as_padding_top);
2728 impl_get_prop!(get_padding_bottom, LayoutPaddingBottomValue, PaddingBottom, as_padding_bottom);
2729 impl_get_prop!(get_padding_left, LayoutPaddingLeftValue, PaddingLeft, as_padding_left);
2730 impl_get_prop!(get_padding_right, LayoutPaddingRightValue, PaddingRight, as_padding_right);
2731 impl_get_prop!(get_margin_top, LayoutMarginTopValue, MarginTop, as_margin_top);
2732 impl_get_prop!(get_margin_bottom, LayoutMarginBottomValue, MarginBottom, as_margin_bottom);
2733 impl_get_prop!(get_margin_left, LayoutMarginLeftValue, MarginLeft, as_margin_left);
2734 impl_get_prop!(get_margin_right, LayoutMarginRightValue, MarginRight, as_margin_right);
2735 impl_get_prop!(get_border_top_width, LayoutBorderTopWidthValue, BorderTopWidth, as_border_top_width);
2736 impl_get_prop!(get_border_left_width, LayoutBorderLeftWidthValue, BorderLeftWidth, as_border_left_width);
2737 impl_get_prop!(get_border_right_width, LayoutBorderRightWidthValue, BorderRightWidth, as_border_right_width);
2738 impl_get_prop!(get_border_bottom_width, LayoutBorderBottomWidthValue, BorderBottomWidth, as_border_bottom_width);
2739 impl_get_prop!(get_overflow_x, LayoutOverflowValue, OverflowX, as_overflow_x);
2740 impl_get_prop!(get_overflow_y, LayoutOverflowValue, OverflowY, as_overflow_y);
2741 impl_get_prop!(get_overflow_block, LayoutOverflowValue, OverflowBlock, as_overflow_block);
2742 impl_get_prop!(get_overflow_inline, LayoutOverflowValue, OverflowInline, as_overflow_inline);
2743 impl_get_prop!(get_flex_direction, LayoutFlexDirectionValue, FlexDirection, as_flex_direction);
2744 impl_get_prop!(get_flex_wrap, LayoutFlexWrapValue, FlexWrap, as_flex_wrap);
2745 impl_get_prop!(get_flex_grow, LayoutFlexGrowValue, FlexGrow, as_flex_grow);
2746 impl_get_prop!(get_flex_shrink, LayoutFlexShrinkValue, FlexShrink, as_flex_shrink);
2747 impl_get_prop!(get_justify_content, LayoutJustifyContentValue, JustifyContent, as_justify_content);
2748 impl_get_prop!(get_align_items, LayoutAlignItemsValue, AlignItems, as_align_items);
2749 impl_get_prop!(get_align_content, LayoutAlignContentValue, AlignContent, as_align_content);
2750 impl_get_prop!(get_mix_blend_mode, StyleMixBlendModeValue, MixBlendMode, as_mix_blend_mode);
2751 impl_get_prop!(get_filter, StyleFilterVecValue, Filter, as_filter);
2752 impl_get_prop!(get_backdrop_filter, StyleFilterVecValue, BackdropFilter, as_backdrop_filter);
2753 impl_get_prop!(get_text_shadow, StyleBoxShadowValue, TextShadow, as_text_shadow);
2754 impl_get_prop!(get_list_style_type, StyleListStyleTypeValue, ListStyleType, as_list_style_type);
2755 impl_get_prop!(get_list_style_position, StyleListStylePositionValue, ListStylePosition, as_list_style_position);
2756 impl_get_prop!(get_table_layout, LayoutTableLayoutValue, TableLayout, as_table_layout);
2757 impl_get_prop!(get_border_collapse, StyleBorderCollapseValue, BorderCollapse, as_border_collapse);
2758 impl_get_prop!(get_border_spacing, LayoutBorderSpacingValue, BorderSpacing, as_border_spacing);
2759 impl_get_prop!(get_caption_side, StyleCaptionSideValue, CaptionSide, as_caption_side);
2760 impl_get_prop!(get_empty_cells, StyleEmptyCellsValue, EmptyCells, as_empty_cells);
2761
2762 pub fn calc_width(
2764 &self,
2765 node_data: &NodeData,
2766 node_id: &NodeId,
2767 styled_node_state: &StyledNodeState,
2768 reference_width: f32,
2769 ) -> f32 {
2770 self.get_width(node_data, node_id, styled_node_state)
2771 .and_then(|w| match w.get_property()? {
2772 LayoutWidth::Px(px) => Some(px.to_pixels_internal(
2773 reference_width,
2774 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2775 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2776 )),
2777 _ => Some(0.0), })
2779 .unwrap_or(0.0)
2780 }
2781
2782 pub fn calc_min_width(
2783 &self,
2784 node_data: &NodeData,
2785 node_id: &NodeId,
2786 styled_node_state: &StyledNodeState,
2787 reference_width: f32,
2788 ) -> f32 {
2789 self.get_min_width(node_data, node_id, styled_node_state)
2790 .and_then(|w| {
2791 Some(w.get_property()?.inner.to_pixels_internal(
2792 reference_width,
2793 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2794 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2795 ))
2796 })
2797 .unwrap_or(0.0)
2798 }
2799
2800 pub fn calc_max_width(
2801 &self,
2802 node_data: &NodeData,
2803 node_id: &NodeId,
2804 styled_node_state: &StyledNodeState,
2805 reference_width: f32,
2806 ) -> Option<f32> {
2807 self.get_max_width(node_data, node_id, styled_node_state)
2808 .and_then(|w| {
2809 Some(w.get_property()?.inner.to_pixels_internal(
2810 reference_width,
2811 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2812 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2813 ))
2814 })
2815 }
2816
2817 pub fn calc_height(
2819 &self,
2820 node_data: &NodeData,
2821 node_id: &NodeId,
2822 styled_node_state: &StyledNodeState,
2823 reference_height: f32,
2824 ) -> f32 {
2825 self.get_height(node_data, node_id, styled_node_state)
2826 .and_then(|h| match h.get_property()? {
2827 LayoutHeight::Px(px) => Some(px.to_pixels_internal(
2828 reference_height,
2829 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2830 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2831 )),
2832 _ => Some(0.0), })
2834 .unwrap_or(0.0)
2835 }
2836
2837 pub fn calc_min_height(
2838 &self,
2839 node_data: &NodeData,
2840 node_id: &NodeId,
2841 styled_node_state: &StyledNodeState,
2842 reference_height: f32,
2843 ) -> f32 {
2844 self.get_min_height(node_data, node_id, styled_node_state)
2845 .and_then(|h| {
2846 Some(h.get_property()?.inner.to_pixels_internal(
2847 reference_height,
2848 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2849 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2850 ))
2851 })
2852 .unwrap_or(0.0)
2853 }
2854
2855 pub fn calc_max_height(
2856 &self,
2857 node_data: &NodeData,
2858 node_id: &NodeId,
2859 styled_node_state: &StyledNodeState,
2860 reference_height: f32,
2861 ) -> Option<f32> {
2862 self.get_max_height(node_data, node_id, styled_node_state)
2863 .and_then(|h| {
2864 Some(h.get_property()?.inner.to_pixels_internal(
2865 reference_height,
2866 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2867 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2868 ))
2869 })
2870 }
2871
2872 pub fn calc_left(
2874 &self,
2875 node_data: &NodeData,
2876 node_id: &NodeId,
2877 styled_node_state: &StyledNodeState,
2878 reference_width: f32,
2879 ) -> Option<f32> {
2880 self.get_left(node_data, node_id, styled_node_state)
2881 .and_then(|l| {
2882 Some(l.get_property()?.inner.to_pixels_internal(
2883 reference_width,
2884 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2885 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2886 ))
2887 })
2888 }
2889
2890 pub fn calc_right(
2891 &self,
2892 node_data: &NodeData,
2893 node_id: &NodeId,
2894 styled_node_state: &StyledNodeState,
2895 reference_width: f32,
2896 ) -> Option<f32> {
2897 self.get_right(node_data, node_id, styled_node_state)
2898 .and_then(|r| {
2899 Some(r.get_property()?.inner.to_pixels_internal(
2900 reference_width,
2901 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2902 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2903 ))
2904 })
2905 }
2906
2907 pub fn calc_top(
2908 &self,
2909 node_data: &NodeData,
2910 node_id: &NodeId,
2911 styled_node_state: &StyledNodeState,
2912 reference_height: f32,
2913 ) -> Option<f32> {
2914 self.get_top(node_data, node_id, styled_node_state)
2915 .and_then(|t| {
2916 Some(t.get_property()?.inner.to_pixels_internal(
2917 reference_height,
2918 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2919 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2920 ))
2921 })
2922 }
2923
2924 pub fn calc_bottom(
2925 &self,
2926 node_data: &NodeData,
2927 node_id: &NodeId,
2928 styled_node_state: &StyledNodeState,
2929 reference_height: f32,
2930 ) -> Option<f32> {
2931 self.get_bottom(node_data, node_id, styled_node_state)
2932 .and_then(|b| {
2933 Some(b.get_property()?.inner.to_pixels_internal(
2934 reference_height,
2935 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2936 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2937 ))
2938 })
2939 }
2940
2941 pub fn calc_border_left_width(
2943 &self,
2944 node_data: &NodeData,
2945 node_id: &NodeId,
2946 styled_node_state: &StyledNodeState,
2947 reference_width: f32,
2948 ) -> f32 {
2949 self.get_border_left_width(node_data, node_id, styled_node_state)
2950 .and_then(|b| {
2951 Some(b.get_property()?.inner.to_pixels_internal(
2952 reference_width,
2953 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2954 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2955 ))
2956 })
2957 .unwrap_or(0.0)
2958 }
2959
2960 pub fn calc_border_right_width(
2961 &self,
2962 node_data: &NodeData,
2963 node_id: &NodeId,
2964 styled_node_state: &StyledNodeState,
2965 reference_width: f32,
2966 ) -> f32 {
2967 self.get_border_right_width(node_data, node_id, styled_node_state)
2968 .and_then(|b| {
2969 Some(b.get_property()?.inner.to_pixels_internal(
2970 reference_width,
2971 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2972 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2973 ))
2974 })
2975 .unwrap_or(0.0)
2976 }
2977
2978 pub fn calc_border_top_width(
2979 &self,
2980 node_data: &NodeData,
2981 node_id: &NodeId,
2982 styled_node_state: &StyledNodeState,
2983 reference_height: f32,
2984 ) -> f32 {
2985 self.get_border_top_width(node_data, node_id, styled_node_state)
2986 .and_then(|b| {
2987 Some(b.get_property()?.inner.to_pixels_internal(
2988 reference_height,
2989 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2990 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
2991 ))
2992 })
2993 .unwrap_or(0.0)
2994 }
2995
2996 pub fn calc_border_bottom_width(
2997 &self,
2998 node_data: &NodeData,
2999 node_id: &NodeId,
3000 styled_node_state: &StyledNodeState,
3001 reference_height: f32,
3002 ) -> f32 {
3003 self.get_border_bottom_width(node_data, node_id, styled_node_state)
3004 .and_then(|b| {
3005 Some(b.get_property()?.inner.to_pixels_internal(
3006 reference_height,
3007 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3008 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3009 ))
3010 })
3011 .unwrap_or(0.0)
3012 }
3013
3014 pub fn calc_padding_left(
3016 &self,
3017 node_data: &NodeData,
3018 node_id: &NodeId,
3019 styled_node_state: &StyledNodeState,
3020 reference_width: f32,
3021 ) -> f32 {
3022 self.get_padding_left(node_data, node_id, styled_node_state)
3023 .and_then(|p| {
3024 Some(p.get_property()?.inner.to_pixels_internal(
3025 reference_width,
3026 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3027 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3028 ))
3029 })
3030 .unwrap_or(0.0)
3031 }
3032
3033 pub fn calc_padding_right(
3034 &self,
3035 node_data: &NodeData,
3036 node_id: &NodeId,
3037 styled_node_state: &StyledNodeState,
3038 reference_width: f32,
3039 ) -> f32 {
3040 self.get_padding_right(node_data, node_id, styled_node_state)
3041 .and_then(|p| {
3042 Some(p.get_property()?.inner.to_pixels_internal(
3043 reference_width,
3044 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3045 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3046 ))
3047 })
3048 .unwrap_or(0.0)
3049 }
3050
3051 pub fn calc_padding_top(
3052 &self,
3053 node_data: &NodeData,
3054 node_id: &NodeId,
3055 styled_node_state: &StyledNodeState,
3056 reference_height: f32,
3057 ) -> f32 {
3058 self.get_padding_top(node_data, node_id, styled_node_state)
3059 .and_then(|p| {
3060 Some(p.get_property()?.inner.to_pixels_internal(
3061 reference_height,
3062 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3063 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3064 ))
3065 })
3066 .unwrap_or(0.0)
3067 }
3068
3069 pub fn calc_padding_bottom(
3070 &self,
3071 node_data: &NodeData,
3072 node_id: &NodeId,
3073 styled_node_state: &StyledNodeState,
3074 reference_height: f32,
3075 ) -> f32 {
3076 self.get_padding_bottom(node_data, node_id, styled_node_state)
3077 .and_then(|p| {
3078 Some(p.get_property()?.inner.to_pixels_internal(
3079 reference_height,
3080 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3081 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3082 ))
3083 })
3084 .unwrap_or(0.0)
3085 }
3086
3087 pub fn calc_margin_left(
3089 &self,
3090 node_data: &NodeData,
3091 node_id: &NodeId,
3092 styled_node_state: &StyledNodeState,
3093 reference_width: f32,
3094 ) -> f32 {
3095 self.get_margin_left(node_data, node_id, styled_node_state)
3096 .and_then(|m| {
3097 Some(m.get_property()?.inner.to_pixels_internal(
3098 reference_width,
3099 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3100 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3101 ))
3102 })
3103 .unwrap_or(0.0)
3104 }
3105
3106 pub fn calc_margin_right(
3107 &self,
3108 node_data: &NodeData,
3109 node_id: &NodeId,
3110 styled_node_state: &StyledNodeState,
3111 reference_width: f32,
3112 ) -> f32 {
3113 self.get_margin_right(node_data, node_id, styled_node_state)
3114 .and_then(|m| {
3115 Some(m.get_property()?.inner.to_pixels_internal(
3116 reference_width,
3117 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3118 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3119 ))
3120 })
3121 .unwrap_or(0.0)
3122 }
3123
3124 pub fn calc_margin_top(
3125 &self,
3126 node_data: &NodeData,
3127 node_id: &NodeId,
3128 styled_node_state: &StyledNodeState,
3129 reference_height: f32,
3130 ) -> f32 {
3131 self.get_margin_top(node_data, node_id, styled_node_state)
3132 .and_then(|m| {
3133 Some(m.get_property()?.inner.to_pixels_internal(
3134 reference_height,
3135 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3136 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3137 ))
3138 })
3139 .unwrap_or(0.0)
3140 }
3141
3142 pub fn calc_margin_bottom(
3143 &self,
3144 node_data: &NodeData,
3145 node_id: &NodeId,
3146 styled_node_state: &StyledNodeState,
3147 reference_height: f32,
3148 ) -> f32 {
3149 self.get_margin_bottom(node_data, node_id, styled_node_state)
3150 .and_then(|m| {
3151 Some(m.get_property()?.inner.to_pixels_internal(
3152 reference_height,
3153 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3154 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3155 ))
3156 })
3157 .unwrap_or(0.0)
3158 }
3159
3160 #[allow(clippy::too_many_lines)] fn resolve_property_dependency(
3162 target_property: &CssProperty,
3163 reference_property: &CssProperty,
3164 ) -> Option<CssProperty> {
3165 #[allow(clippy::wildcard_imports)]
3168 use azul_css::{
3169 css::CssPropertyValue,
3170 props::{
3171 basic::{font::StyleFontSize, length::SizeMetric, pixel::PixelValue},
3172 layout::*,
3173 style::{SelectionRadius, StyleLetterSpacing, StyleWordSpacing},
3174 },
3175 };
3176
3177 let get_pixel_value = |prop: &CssProperty| -> Option<PixelValue> {
3179 match prop {
3180 CssProperty::FontSize(val) => val.get_property().map(|v| v.inner),
3181 CssProperty::LetterSpacing(val) => val.get_property().map(|v| v.inner),
3182 CssProperty::WordSpacing(val) => val.get_property().map(|v| v.inner),
3183 CssProperty::PaddingLeft(val) => val.get_property().map(|v| v.inner),
3184 CssProperty::PaddingRight(val) => val.get_property().map(|v| v.inner),
3185 CssProperty::PaddingTop(val) => val.get_property().map(|v| v.inner),
3186 CssProperty::PaddingBottom(val) => val.get_property().map(|v| v.inner),
3187 CssProperty::MarginLeft(val) => val.get_property().map(|v| v.inner),
3188 CssProperty::MarginRight(val) => val.get_property().map(|v| v.inner),
3189 CssProperty::MarginTop(val) => val.get_property().map(|v| v.inner),
3190 CssProperty::MarginBottom(val) => val.get_property().map(|v| v.inner),
3191 CssProperty::MinWidth(val) => val.get_property().map(|v| v.inner),
3192 CssProperty::MinHeight(val) => val.get_property().map(|v| v.inner),
3193 CssProperty::MaxWidth(val) => val.get_property().map(|v| v.inner),
3194 CssProperty::MaxHeight(val) => val.get_property().map(|v| v.inner),
3195 CssProperty::SelectionRadius(val) => val.get_property().map(|v| v.inner),
3196 _ => None,
3197 }
3198 };
3199
3200 let target_pixel_value = get_pixel_value(target_property)?;
3201 let reference_pixel_value = get_pixel_value(reference_property)?;
3202
3203 let reference_px = match reference_pixel_value.metric {
3205 SizeMetric::Px => reference_pixel_value.number.get(),
3206 SizeMetric::Pt => reference_pixel_value.number.get() * PT_TO_PX,
3207 SizeMetric::In => reference_pixel_value.number.get() * IN_TO_PX,
3208 SizeMetric::Cm => reference_pixel_value.number.get() * CM_TO_PX,
3209 SizeMetric::Mm => reference_pixel_value.number.get() * MM_TO_PX,
3210 SizeMetric::Em
3212 | SizeMetric::Rem
3213 | SizeMetric::Percent
3214 | SizeMetric::Vw
3215 | SizeMetric::Vh
3216 | SizeMetric::Vmin
3217 | SizeMetric::Vmax => return None,
3218 };
3219
3220 let resolved_px = match target_pixel_value.metric {
3222 SizeMetric::Px => target_pixel_value.number.get(),
3223 SizeMetric::Pt => target_pixel_value.number.get() * PT_TO_PX,
3224 SizeMetric::In => target_pixel_value.number.get() * IN_TO_PX,
3225 SizeMetric::Cm => target_pixel_value.number.get() * CM_TO_PX,
3226 SizeMetric::Mm => target_pixel_value.number.get() * MM_TO_PX,
3227 SizeMetric::Em | SizeMetric::Rem => target_pixel_value.number.get() * reference_px,
3229 SizeMetric::Percent => target_pixel_value.number.get() / 100.0 * reference_px,
3230 SizeMetric::Vw | SizeMetric::Vh | SizeMetric::Vmin | SizeMetric::Vmax => return None,
3232 };
3233
3234 let resolved_pixel_value = PixelValue::px(resolved_px);
3236
3237 match target_property {
3238 CssProperty::FontSize(_) => Some(CssProperty::FontSize(CssPropertyValue::Exact(
3239 StyleFontSize {
3240 inner: resolved_pixel_value,
3241 },
3242 ))),
3243 CssProperty::LetterSpacing(_) => Some(CssProperty::LetterSpacing(
3244 CssPropertyValue::Exact(StyleLetterSpacing {
3245 inner: resolved_pixel_value,
3246 }),
3247 )),
3248 CssProperty::WordSpacing(_) => Some(CssProperty::WordSpacing(CssPropertyValue::Exact(
3249 StyleWordSpacing {
3250 inner: resolved_pixel_value,
3251 },
3252 ))),
3253 CssProperty::PaddingLeft(_) => Some(CssProperty::PaddingLeft(CssPropertyValue::Exact(
3254 LayoutPaddingLeft {
3255 inner: resolved_pixel_value,
3256 },
3257 ))),
3258 CssProperty::PaddingRight(_) => Some(CssProperty::PaddingRight(
3259 CssPropertyValue::Exact(LayoutPaddingRight {
3260 inner: resolved_pixel_value,
3261 }),
3262 )),
3263 CssProperty::PaddingTop(_) => Some(CssProperty::PaddingTop(CssPropertyValue::Exact(
3264 LayoutPaddingTop {
3265 inner: resolved_pixel_value,
3266 },
3267 ))),
3268 CssProperty::PaddingBottom(_) => Some(CssProperty::PaddingBottom(
3269 CssPropertyValue::Exact(LayoutPaddingBottom {
3270 inner: resolved_pixel_value,
3271 }),
3272 )),
3273 CssProperty::MarginLeft(_) => Some(CssProperty::MarginLeft(CssPropertyValue::Exact(
3274 LayoutMarginLeft {
3275 inner: resolved_pixel_value,
3276 },
3277 ))),
3278 CssProperty::MarginRight(_) => Some(CssProperty::MarginRight(CssPropertyValue::Exact(
3279 LayoutMarginRight {
3280 inner: resolved_pixel_value,
3281 },
3282 ))),
3283 CssProperty::MarginTop(_) => Some(CssProperty::MarginTop(CssPropertyValue::Exact(
3284 LayoutMarginTop {
3285 inner: resolved_pixel_value,
3286 },
3287 ))),
3288 CssProperty::MarginBottom(_) => Some(CssProperty::MarginBottom(
3289 CssPropertyValue::Exact(LayoutMarginBottom {
3290 inner: resolved_pixel_value,
3291 }),
3292 )),
3293 CssProperty::MinWidth(_) => Some(CssProperty::MinWidth(CssPropertyValue::Exact(
3294 LayoutMinWidth {
3295 inner: resolved_pixel_value,
3296 },
3297 ))),
3298 CssProperty::MinHeight(_) => Some(CssProperty::MinHeight(CssPropertyValue::Exact(
3299 LayoutMinHeight {
3300 inner: resolved_pixel_value,
3301 },
3302 ))),
3303 CssProperty::MaxWidth(_) => Some(CssProperty::MaxWidth(CssPropertyValue::Exact(
3304 LayoutMaxWidth {
3305 inner: resolved_pixel_value,
3306 },
3307 ))),
3308 CssProperty::MaxHeight(_) => Some(CssProperty::MaxHeight(CssPropertyValue::Exact(
3309 LayoutMaxHeight {
3310 inner: resolved_pixel_value,
3311 },
3312 ))),
3313 CssProperty::SelectionRadius(_) => Some(CssProperty::SelectionRadius(
3314 CssPropertyValue::Exact(SelectionRadius {
3315 inner: resolved_pixel_value,
3316 }),
3317 )),
3318 _ => None,
3319 }
3320 }
3321
3322 pub fn apply_ua_css(&mut self, node_data: &[NodeData]) {
3332 use azul_css::props::property::CssPropertyType;
3333 use azul_css::dynamic_selector::PseudoStateType;
3334
3335 let node_count = node_data.len();
3336 if node_count == 0 {
3337 return;
3338 }
3339
3340 let mut prop_set: Vec<[u128; 2]> = vec![[0u128; 2]; node_count];
3343
3344 for (node_idx, props) in self.css_props.iter_node_slices() {
3346 for p in props {
3347 if p.state == PseudoStateType::Normal {
3348 let d = p.prop_type as u16 as usize;
3349 if d < 128 {
3350 prop_set[node_idx][0] |= 1u128 << d;
3351 } else {
3352 prop_set[node_idx][1] |= 1u128 << (d - 128);
3353 }
3354 }
3355 }
3356 }
3357
3358 for (node_idx, props) in self.cascaded_props.iter_node_slices() {
3360 for p in props {
3361 if p.state == PseudoStateType::Normal {
3362 let d = p.prop_type as u16 as usize;
3363 if d < 128 {
3364 prop_set[node_idx][0] |= 1u128 << d;
3365 } else {
3366 prop_set[node_idx][1] |= 1u128 << (d - 128);
3367 }
3368 }
3369 }
3370 }
3371
3372 for (node_idx, node) in node_data.iter().enumerate() {
3374 for (prop, conds) in node.style.iter_inline_properties() {
3375 let is_normal = conds.as_slice().is_empty();
3376 if is_normal {
3377 let d = prop.get_type() as u16 as usize;
3378 if d < 128 {
3379 prop_set[node_idx][0] |= 1u128 << d;
3380 } else {
3381 prop_set[node_idx][1] |= 1u128 << (d - 128);
3382 }
3383 }
3384 }
3385 }
3386
3387 let property_types = [
3389 CssPropertyType::Display,
3390 CssPropertyType::Width,
3391 CssPropertyType::Height,
3392 CssPropertyType::FontSize,
3393 CssPropertyType::FontWeight,
3394 CssPropertyType::FontFamily,
3395 CssPropertyType::MarginTop,
3396 CssPropertyType::MarginBottom,
3397 CssPropertyType::MarginLeft,
3398 CssPropertyType::MarginRight,
3399 CssPropertyType::PaddingTop,
3400 CssPropertyType::PaddingBottom,
3401 CssPropertyType::PaddingLeft,
3402 CssPropertyType::PaddingRight,
3403 CssPropertyType::BorderTopStyle,
3404 CssPropertyType::BorderTopWidth,
3405 CssPropertyType::BorderTopColor,
3406 CssPropertyType::BreakInside,
3407 CssPropertyType::BreakAfter,
3408 CssPropertyType::ListStyleType,
3409 CssPropertyType::CounterReset,
3410 CssPropertyType::TextDecoration,
3411 CssPropertyType::TextAlign,
3412 CssPropertyType::VerticalAlign,
3413 CssPropertyType::Cursor,
3414 ];
3415
3416 for (node_index, node) in node_data.iter().enumerate() {
3418 let node_type = &node.node_type;
3419
3420 for prop_type in &property_types {
3421 let d = *prop_type as u16 as usize;
3423 let has_prop = if d < 128 {
3424 (prop_set[node_index][0] & (1u128 << d)) != 0
3425 } else {
3426 (prop_set[node_index][1] & (1u128 << (d - 128))) != 0
3427 };
3428
3429 if has_prop {
3430 continue;
3431 }
3432
3433 if let Some(ua_prop) = crate::ua_css::get_ua_property(node_type, *prop_type) {
3435 self.cascaded_props.push_to(node_index, StatefulCssProperty {
3436 state: PseudoStateType::Normal,
3437 prop_type: *prop_type,
3438 property: ua_prop.clone(),
3439 });
3440
3441 if d < 128 {
3443 prop_set[node_index][0] |= 1u128 << d;
3444 } else {
3445 prop_set[node_index][1] |= 1u128 << (d - 128);
3446 }
3447 }
3448 }
3449 }
3450 }
3451
3452 pub fn sort_cascaded_props(&mut self) {
3455 self.cascaded_props.sort_each_and_flatten(|p| (p.state, p.prop_type));
3456 }
3457
3458 pub fn compute_inherited_values(
3464 &mut self,
3465 node_hierarchy: &[NodeHierarchyItem],
3466 node_data: &[NodeData],
3467 ) -> Vec<NodeId> {
3468 if self.computed_values.len() < node_hierarchy.len() {
3469 self.computed_values.resize(node_hierarchy.len(), Vec::new());
3470 }
3471 node_hierarchy
3472 .iter()
3473 .enumerate()
3474 .filter_map(|(node_index, hierarchy_item)| {
3475 let node_id = NodeId::new(node_index);
3476 let parent_id = hierarchy_item.parent_id();
3477 let parent_computed: Option<Vec<(CssPropertyType, CssPropertyWithOrigin)>> =
3478 parent_id.and_then(|pid| self.computed_values.get(pid.index()).cloned());
3479
3480 let mut ctx = InheritanceContext {
3481 node_id,
3482 parent_id,
3483 computed_values: Vec::new(),
3484 };
3485
3486 if let Some(ref parent_values) = parent_computed {
3488 Self::inherit_from_parent(&mut ctx, parent_values);
3489 }
3490
3491 self.apply_cascade_properties(
3493 &mut ctx,
3494 node_id,
3495 parent_computed.as_ref(),
3496 node_data,
3497 node_index,
3498 );
3499
3500 let changed = self.store_if_changed(&ctx);
3502 changed.then_some(node_id)
3503 })
3504 .collect()
3505 }
3506
3507 fn inherit_from_parent(
3509 ctx: &mut InheritanceContext,
3510 parent_values: &[(CssPropertyType, CssPropertyWithOrigin)],
3511 ) {
3512 for (prop_type, prop_with_origin) in
3513 parent_values.iter().filter(|(pt, _)| pt.is_inheritable())
3514 {
3515 let entry = (*prop_type, CssPropertyWithOrigin {
3516 property: prop_with_origin.property.clone(),
3517 origin: CssPropertyOrigin::Inherited,
3518 });
3519 match ctx.computed_values.binary_search_by_key(prop_type, |(k, _)| *k) {
3521 Ok(idx) => ctx.computed_values[idx] = entry,
3522 Err(idx) => ctx.computed_values.insert(idx, entry),
3523 }
3524 }
3525 }
3526
3527 fn apply_cascade_properties(
3529 &self,
3530 ctx: &mut InheritanceContext,
3531 node_id: NodeId,
3532 parent_computed: Option<&Vec<(CssPropertyType, CssPropertyWithOrigin)>>,
3533 node_data: &[NodeData],
3534 node_index: usize,
3535 ) {
3536 {
3538 let cascaded_slice = self.cascaded_props.get_slice(node_id.index());
3539 for p in cascaded_slice {
3540 if p.state == azul_css::dynamic_selector::PseudoStateType::Normal
3541 && Self::should_apply_cascaded(&ctx.computed_values, p.prop_type, &p.property) {
3542 Self::process_property(ctx, &p.property, parent_computed);
3543 }
3544 }
3545 }
3546
3547 {
3549 let css_slice = self.css_props.get_slice(node_id.index());
3550 for p in css_slice {
3551 if p.state == azul_css::dynamic_selector::PseudoStateType::Normal {
3552 Self::process_property(ctx, &p.property, parent_computed);
3553 }
3554 }
3555 }
3556
3557 for (prop, conds) in node_data[node_index].style.iter_inline_properties() {
3559 if conds.as_slice().is_empty() {
3561 Self::process_property(ctx, prop, parent_computed);
3562 }
3563 }
3564
3565 if let Some(user_props) = self.user_overridden_properties.get(node_id.index()) {
3567 for (_, prop) in user_props {
3568 Self::process_property(ctx, prop, parent_computed);
3569 }
3570 }
3571 }
3572
3573 fn should_apply_cascaded(
3586 computed: &[(CssPropertyType, CssPropertyWithOrigin)],
3587 prop_type: CssPropertyType,
3588 _prop: &CssProperty,
3589 ) -> bool {
3590 computed
3591 .binary_search_by_key(&prop_type, |(k, _)| *k)
3592 .map_or(true, |idx| computed[idx].1.origin == CssPropertyOrigin::Inherited)
3593 }
3594
3595 fn process_property(
3597 ctx: &mut InheritanceContext,
3598 prop: &CssProperty,
3599 parent_computed: Option<&Vec<(CssPropertyType, CssPropertyWithOrigin)>>,
3600 ) {
3601 let prop_type = prop.get_type();
3602
3603 let resolved = if prop_type == CssPropertyType::FontSize {
3604 Self::resolve_font_size_property(prop, parent_computed)
3605 } else {
3606 Self::resolve_other_property(prop, &ctx.computed_values)
3607 };
3608
3609 let entry = (prop_type, CssPropertyWithOrigin {
3610 property: resolved,
3611 origin: CssPropertyOrigin::Own,
3612 });
3613 match ctx.computed_values.binary_search_by_key(&prop_type, |(k, _)| *k) {
3614 Ok(idx) => ctx.computed_values[idx] = entry,
3615 Err(idx) => ctx.computed_values.insert(idx, entry),
3616 }
3617 }
3618
3619 fn resolve_font_size_property(
3621 prop: &CssProperty,
3622 parent_computed: Option<&Vec<(CssPropertyType, CssPropertyWithOrigin)>>,
3623 ) -> CssProperty {
3624 let parent_font_size = parent_computed
3625 .and_then(|p| {
3626 p.binary_search_by_key(&CssPropertyType::FontSize, |(k, _)| *k)
3627 .ok()
3628 .map(|idx| &p[idx].1)
3629 });
3630
3631 parent_font_size.map_or_else(|| Self::resolve_font_size_to_pixels(
3632 prop,
3633 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3634 ), |pfs| Self::resolve_property_dependency(prop, &pfs.property).unwrap_or_else(
3635 || {
3636 Self::resolve_font_size_to_pixels(
3637 prop,
3638 azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
3639 )
3640 },
3641 ))
3642 }
3643
3644 fn resolve_other_property(
3646 prop: &CssProperty,
3647 computed: &[(CssPropertyType, CssPropertyWithOrigin)],
3648 ) -> CssProperty {
3649 computed
3650 .binary_search_by_key(&CssPropertyType::FontSize, |(k, _)| *k)
3651 .ok()
3652 .and_then(|idx| Self::resolve_property_dependency(prop, &computed[idx].1.property))
3653 .unwrap_or_else(|| prop.clone())
3654 }
3655
3656 fn resolve_font_size_to_pixels(prop: &CssProperty, reference_px: f32) -> CssProperty {
3658 use azul_css::{
3659 css::CssPropertyValue,
3660 props::basic::{font::StyleFontSize, length::SizeMetric, pixel::PixelValue},
3661 };
3662
3663 let CssProperty::FontSize(css_val) = prop else {
3664 return prop.clone();
3665 };
3666
3667 let Some(font_size) = css_val.get_property() else {
3668 return prop.clone();
3669 };
3670
3671 let resolved_px = match font_size.inner.metric {
3672 SizeMetric::Px => font_size.inner.number.get(),
3673 SizeMetric::Pt => font_size.inner.number.get() * PT_TO_PX,
3674 SizeMetric::In => font_size.inner.number.get() * IN_TO_PX,
3675 SizeMetric::Cm => font_size.inner.number.get() * CM_TO_PX,
3676 SizeMetric::Mm => font_size.inner.number.get() * MM_TO_PX,
3677 SizeMetric::Em => font_size.inner.number.get() * reference_px,
3678 SizeMetric::Rem => {
3679 font_size.inner.number.get() * azul_css::props::basic::pixel::DEFAULT_FONT_SIZE
3680 }
3681 SizeMetric::Percent => font_size.inner.number.get() / 100.0 * reference_px,
3682 SizeMetric::Vw | SizeMetric::Vh | SizeMetric::Vmin | SizeMetric::Vmax => {
3683 return prop.clone();
3684 }
3685 };
3686
3687 CssProperty::FontSize(CssPropertyValue::Exact(StyleFontSize {
3688 inner: PixelValue::px(resolved_px),
3689 }))
3690 }
3691
3692 fn has_relative_font_size_unit(prop: &CssProperty) -> bool {
3694 use azul_css::props::basic::length::SizeMetric;
3695
3696 let CssProperty::FontSize(css_val) = prop else {
3697 return false;
3698 };
3699
3700 css_val
3701 .get_property()
3702 .is_some_and(|fs| {
3703 matches!(
3704 fs.inner.metric,
3705 SizeMetric::Em | SizeMetric::Rem | SizeMetric::Percent
3706 )
3707 })
3708 }
3709
3710 fn store_if_changed(&mut self, ctx: &InheritanceContext) -> bool {
3712 let values_changed = self
3713 .computed_values
3714 .get(ctx.node_id.index()) != Some(&ctx.computed_values);
3715
3716 self.computed_values[ctx.node_id.index()].clone_from(&ctx.computed_values);
3717
3718 values_changed
3719 }
3720}
3721
3722struct InheritanceContext {
3724 node_id: NodeId,
3725 parent_id: Option<NodeId>,
3726 computed_values: Vec<(CssPropertyType, CssPropertyWithOrigin)>,
3727}
3728
3729impl CssPropertyCache {
3730
3731 pub(crate) fn invalidate_resolved_cache(&mut self) {
3733 self.compact_cache = None;
3734 }
3735}
3736
3737#[cfg(test)]
3738#[allow(clippy::float_cmp, clippy::too_many_lines)]
3739mod autotest_generated {
3740 use azul_css::{
3741 css::CssPropertyValue,
3742 dynamic_selector::{
3743 CssPropertyWithConditions, DynamicSelector, DynamicSelectorContext, PseudoStateType,
3744 },
3745 props::{
3746 basic::{length::SizeMetric, pixel::PixelValue},
3747 layout::{
3748 LayoutFlexBasis, LayoutInsetBottom, LayoutLeft, LayoutMarginTop, LayoutMaxWidth,
3749 LayoutMinWidth, LayoutOverflow, LayoutPaddingLeft, LayoutRight, LayoutTop,
3750 },
3751 style::LayoutBorderLeftWidth,
3752 },
3753 };
3754
3755 use super::*;
3756
3757 fn close(a: f32, b: f32) -> bool {
3764 (a - b).abs() < 0.01
3765 }
3766
3767 fn n0() -> NodeId {
3768 NodeId::new(0)
3769 }
3770
3771 fn normal() -> StyledNodeState {
3772 StyledNodeState::default()
3773 }
3774
3775 fn div_with(props: Vec<CssProperty>) -> NodeData {
3777 let mut nd = NodeData::create_div();
3778 for property in props {
3779 nd.add_css_property(CssPropertyWithConditions {
3780 property,
3781 apply_if: Vec::new().into(),
3782 });
3783 }
3784 nd
3785 }
3786
3787 fn div_with_pseudo(props: Vec<CssProperty>, state: PseudoStateType) -> NodeData {
3789 let mut nd = NodeData::create_div();
3790 for property in props {
3791 nd.add_css_property(CssPropertyWithConditions {
3792 property,
3793 apply_if: vec![DynamicSelector::PseudoState(state)].into(),
3794 });
3795 }
3796 nd
3797 }
3798
3799 fn width_px(v: f32) -> CssProperty {
3800 CssProperty::Width(CssPropertyValue::Exact(LayoutWidth::Px(PixelValue::px(v))))
3801 }
3802
3803 fn width_pct(v: f32) -> CssProperty {
3804 CssProperty::Width(CssPropertyValue::Exact(LayoutWidth::Px(PixelValue::percent(
3805 v,
3806 ))))
3807 }
3808
3809 fn font_size(pv: PixelValue) -> CssProperty {
3810 CssProperty::FontSize(CssPropertyValue::Exact(StyleFontSize { inner: pv }))
3811 }
3812
3813 fn font_size_parts(p: &CssProperty) -> Option<(SizeMetric, f32)> {
3815 match p {
3816 CssProperty::FontSize(v) => v
3817 .get_property()
3818 .map(|fs| (fs.inner.metric, fs.inner.number.get())),
3819 _ => None,
3820 }
3821 }
3822
3823 fn stateful(state: PseudoStateType, property: CssProperty) -> StatefulCssProperty {
3824 StatefulCssProperty {
3825 state,
3826 prop_type: property.get_type(),
3827 property,
3828 }
3829 }
3830
3831 #[test]
3836 fn flatvecvec_new_zero_is_empty() {
3837 let f = FlatVecVec::<i32>::new(0);
3838 assert_eq!(f.len(), 0);
3839 assert!(f.is_empty());
3840 assert!(f.is_flattened());
3843 assert!(f.get_slice(0).is_empty());
3844 }
3845
3846 #[test]
3847 fn flatvecvec_new_invariants_hold() {
3848 let f = FlatVecVec::<i32>::new(3);
3849 assert_eq!(f.len(), 3);
3850 assert!(!f.is_empty());
3851 assert!(!f.is_flattened(), "fresh multi-slot vec is in build phase");
3852 assert_eq!(f.build_get(0), Some(&Vec::new()));
3853 assert_eq!(f.build_get(2), Some(&Vec::new()));
3854 assert_eq!(f.build_get(3), None, "one past the end");
3855 assert_eq!(f.build_get(usize::MAX), None);
3856 assert!(f.get_slice(0).is_empty());
3857 }
3858
3859 #[test]
3860 fn flatvecvec_default_is_neutral() {
3861 let f = FlatVecVec::<i32>::default();
3862 assert_eq!(f.len(), 0);
3863 assert!(f.is_empty());
3864 assert_eq!(f.build_get(0), None);
3865 assert!(f.get_slice(0).is_empty());
3866 }
3867
3868 #[test]
3869 fn flatvecvec_get_slice_out_of_bounds_is_empty_in_both_phases() {
3870 let mut f = FlatVecVec::<i32>::new(2);
3871 f.push_to(0, 7);
3872 assert_eq!(f.get_slice(0), &[7]);
3874 assert!(f.get_slice(2).is_empty());
3875 assert!(f.get_slice(usize::MAX).is_empty());
3876
3877 f.flatten();
3878 assert_eq!(f.get_slice(0), &[7]);
3880 assert!(f.get_slice(2).is_empty());
3881 assert!(f.get_slice(usize::MAX).is_empty());
3882 }
3883
3884 #[test]
3885 #[should_panic(expected = "index out of bounds")]
3886 fn flatvecvec_push_to_out_of_bounds_panics() {
3887 let mut f = FlatVecVec::<i32>::new(1);
3889 f.push_to(1, 0);
3890 }
3891
3892 #[test]
3893 #[should_panic(expected = "index out of bounds")]
3894 fn flatvecvec_push_to_after_flatten_panics() {
3895 let mut f = FlatVecVec::<i32>::new(1);
3897 f.flatten();
3898 f.push_to(0, 0);
3899 }
3900
3901 #[test]
3902 #[should_panic(expected = "index out of bounds")]
3903 fn flatvecvec_build_mut_out_of_bounds_panics() {
3904 let mut f = FlatVecVec::<i32>::new(1);
3905 let _ = f.build_mut(usize::MAX);
3906 }
3907
3908 #[test]
3909 fn flatvecvec_build_iter_mut_visits_every_slot() {
3910 let mut f = FlatVecVec::<i32>::new(3);
3911 f.push_to(0, 1);
3912 f.push_to(2, 2);
3913 let mut visited = 0;
3914 for v in f.build_iter_mut() {
3915 visited += 1;
3916 v.clear();
3917 }
3918 assert_eq!(visited, 3);
3919 assert!(f.get_slice(0).is_empty());
3920 assert!(f.get_slice(2).is_empty());
3921 }
3922
3923 #[test]
3924 fn flatvecvec_build_get_returns_none_once_flattened() {
3925 let mut f = FlatVecVec::<i32>::new(1);
3926 f.push_to(0, 5);
3927 f.flatten();
3928 assert_eq!(f.build_get(0), None);
3930 assert_eq!(f.get_slice(0), &[5]);
3931 }
3932
3933 #[test]
3938 fn flatvecvec_heap_bytes_zero_and_empty() {
3939 let f = FlatVecVec::<i32>::default();
3940 assert_eq!(f.heap_bytes(0), 0, "empty vec, zero element size");
3941 assert_eq!(f.heap_bytes(usize::MAX), 0);
3944 assert_eq!(f.heap_bytes(size_of::<i32>()), 0);
3945 }
3946
3947 #[test]
3948 fn flatvecvec_heap_bytes_counts_build_and_flat_storage() {
3949 let mut f = FlatVecVec::<i32>::new(4);
3950 assert!(f.heap_bytes(0) >= 4 * size_of::<Vec<i32>>());
3953
3954 f.push_to(0, 1);
3955 f.push_to(0, 2);
3956 let build_bytes = f.heap_bytes(size_of::<i32>());
3957 assert!(build_bytes > 0);
3958
3959 f.flatten();
3960 let flat_bytes = f.heap_bytes(size_of::<i32>());
3962 assert!(flat_bytes >= 2 * size_of::<i32>() + 4 * size_of::<(u32, u32)>());
3963 }
3964
3965 #[test]
3970 fn flatvecvec_sort_each_and_flatten_keeps_last_of_equal_keys() {
3971 let mut f = FlatVecVec::<(i32, i32)>::new(1);
3973 f.push_to(0, (1, 10));
3974 f.push_to(0, (1, 20)); f.push_to(0, (0, 30));
3976 f.sort_each_and_flatten(|p| p.0);
3977
3978 assert!(f.is_flattened());
3979 assert_eq!(f.get_slice(0), &[(0, 30), (1, 20)]);
3980 }
3981
3982 #[test]
3983 fn flatvecvec_sort_each_and_flatten_on_empty_slots() {
3984 let mut f = FlatVecVec::<i32>::new(3);
3985 f.push_to(1, 42);
3986 f.sort_each_and_flatten(|v| *v);
3987 assert_eq!(f.len(), 3);
3988 assert!(f.get_slice(0).is_empty());
3989 assert_eq!(f.get_slice(1), &[42]);
3990 assert!(f.get_slice(2).is_empty());
3991 }
3992
3993 #[test]
3994 fn flatvecvec_sort_each_and_flatten_on_zero_nodes_does_not_panic() {
3995 let mut f = FlatVecVec::<i32>::new(0);
3996 f.sort_each_and_flatten(|v| *v);
3997 assert_eq!(f.len(), 0);
3998 assert!(f.get_slice(0).is_empty());
3999 }
4000
4001 #[test]
4002 fn flatvecvec_flatten_does_not_deduplicate() {
4003 let mut f = FlatVecVec::<i32>::new(2);
4004 f.push_to(0, 5);
4005 f.push_to(0, 5);
4006 f.push_to(1, 9);
4007 f.flatten();
4008 assert!(f.is_flattened());
4009 assert_eq!(f.get_slice(0), &[5, 5], "flatten() must not dedup");
4010 assert_eq!(f.get_slice(1), &[9]);
4011 }
4012
4013 #[test]
4018 fn flatvecvec_retain_before_flatten_is_a_noop() {
4019 let mut f = FlatVecVec::<i32>::new(1);
4022 f.push_to(0, 1);
4023 f.push_to(0, 2);
4024 f.retain(|_| false);
4025 assert_eq!(f.get_slice(0), &[1, 2], "build-phase data left untouched");
4026 }
4027
4028 #[test]
4029 fn flatvecvec_retain_preserves_per_node_order() {
4030 let mut f = FlatVecVec::<i32>::new(2);
4031 for v in [1, 2, 3, 4] {
4032 f.push_to(0, v);
4033 }
4034 f.push_to(1, 5);
4035 f.flatten();
4036
4037 f.retain(|v| v % 2 == 0);
4038 assert_eq!(f.get_slice(0), &[2, 4]);
4039 assert!(f.get_slice(1).is_empty());
4040 assert_eq!(f.len(), 2, "node slots survive an empty retain");
4041 }
4042
4043 #[test]
4044 fn flatvecvec_retain_dropping_everything_leaves_empty_slices() {
4045 let mut f = FlatVecVec::<i32>::new(2);
4046 f.push_to(0, 1);
4047 f.push_to(1, 2);
4048 f.flatten();
4049 f.retain(|_| false);
4050 assert_eq!(f.len(), 2);
4051 assert!(f.get_slice(0).is_empty());
4052 assert!(f.get_slice(1).is_empty());
4053 }
4054
4055 #[test]
4056 fn flatvecvec_retain_with_node_index_sees_owning_node() {
4057 let mut f = FlatVecVec::<i32>::new(3);
4058 f.push_to(0, 10);
4059 f.push_to(1, 11);
4060 f.push_to(2, 12);
4061 f.flatten();
4062
4063 f.retain_with_node_index(|idx, _| idx == 1);
4064 assert!(f.get_slice(0).is_empty());
4065 assert_eq!(f.get_slice(1), &[11]);
4066 assert!(f.get_slice(2).is_empty());
4067 }
4068
4069 #[test]
4070 fn flatvecvec_retain_with_node_index_before_flatten_is_a_noop() {
4071 let mut f = FlatVecVec::<i32>::new(1);
4072 f.push_to(0, 1);
4073 f.retain_with_node_index(|_, _| false);
4074 assert_eq!(f.get_slice(0), &[1]);
4075 }
4076
4077 #[test]
4082 fn flatvecvec_iter_node_slices_covers_all_nodes_in_both_phases() {
4083 let mut f = FlatVecVec::<i32>::new(3);
4084 f.push_to(1, 7);
4085
4086 let build: Vec<(usize, Vec<i32>)> = f
4087 .iter_node_slices()
4088 .map(|(i, s)| (i, s.to_vec()))
4089 .collect();
4090 assert_eq!(build, vec![(0, vec![]), (1, vec![7]), (2, vec![])]);
4091
4092 f.flatten();
4093 let flat: Vec<(usize, Vec<i32>)> = f
4094 .iter_node_slices()
4095 .map(|(i, s)| (i, s.to_vec()))
4096 .collect();
4097 assert_eq!(flat, build, "iteration is phase-independent");
4098 }
4099
4100 #[test]
4101 fn flatvecvec_iter_node_slices_on_empty_yields_nothing() {
4102 let f = FlatVecVec::<i32>::new(0);
4103 assert_eq!(f.iter_node_slices().count(), 0);
4104 }
4105
4106 #[test]
4107 fn flatvecvec_extend_from_both_in_build_phase() {
4108 let mut a = FlatVecVec::<i32>::new(1);
4109 a.push_to(0, 1);
4110 let mut b = FlatVecVec::<i32>::new(2);
4111 b.push_to(0, 2);
4112 b.push_to(1, 3);
4113
4114 a.extend_from(&mut b);
4115 assert_eq!(a.len(), 3);
4116 assert_eq!(a.get_slice(0), &[1]);
4117 assert_eq!(a.get_slice(1), &[2]);
4118 assert_eq!(a.get_slice(2), &[3]);
4119 assert_eq!(b.len(), 0, "other is drained");
4120 }
4121
4122 #[test]
4123 fn flatvecvec_extend_from_both_flattened_rebases_offsets() {
4124 let mut a = FlatVecVec::<i32>::new(2);
4125 a.push_to(0, 1);
4126 a.push_to(1, 2);
4127 a.flatten();
4128
4129 let mut b = FlatVecVec::<i32>::new(2);
4130 b.push_to(0, 3);
4131 b.push_to(1, 4);
4132 b.flatten();
4133
4134 a.extend_from(&mut b);
4135 assert_eq!(a.len(), 4);
4136 assert_eq!(a.get_slice(0), &[1]);
4137 assert_eq!(a.get_slice(1), &[2]);
4138 assert_eq!(a.get_slice(2), &[3], "offsets rebased onto a's flat data");
4139 assert_eq!(a.get_slice(3), &[4]);
4140 }
4141
4142 #[test]
4143 fn flatvecvec_extend_from_across_phases_discards_self_flat_data() {
4144 let mut a = FlatVecVec::<i32>::new(1);
4148 a.push_to(0, 1);
4149 a.flatten();
4150
4151 let mut b = FlatVecVec::<i32>::new(1);
4152 b.push_to(0, 2);
4153
4154 a.extend_from(&mut b); assert_eq!(a.len(), 1);
4156 assert_eq!(
4157 a.get_slice(0),
4158 &[2],
4159 "a's own flattened item (1) is silently lost"
4160 );
4161 }
4162
4163 #[test]
4164 fn flatvecvec_eq_within_the_same_phase() {
4165 let mut a = FlatVecVec::<i32>::new(1);
4166 a.push_to(0, 1);
4167 let mut b = FlatVecVec::<i32>::new(1);
4168 b.push_to(0, 1);
4169 assert_eq!(a, b);
4170
4171 b.push_to(0, 2);
4172 assert_ne!(a, b);
4173
4174 a.flatten();
4175 let mut c = FlatVecVec::<i32>::new(1);
4178 c.push_to(0, 1);
4179 c.flatten();
4180 assert_eq!(a, c);
4181 }
4182
4183 #[test]
4188 fn breakdown_total_bytes_sums_subfields_and_excludes_node_count() {
4189 let b = CssPropertyCacheBreakdown {
4190 node_count: 999_999,
4191 cascaded_props_bytes: 1,
4192 css_props_bytes: 2,
4193 computed_values_bytes: 4,
4194 user_overridden_bytes: 8,
4195 global_css_props_bytes: 16,
4196 compact_cache_bytes: 32,
4197 resolved_font_sizes_bytes: 64,
4198 };
4199 assert_eq!(b.total_bytes(), 127, "node_count is not a byte count");
4200 }
4201
4202 #[test]
4203 fn breakdown_total_bytes_default_is_zero_and_max_single_field_does_not_overflow() {
4204 assert_eq!(CssPropertyCacheBreakdown::default().total_bytes(), 0);
4205
4206 let b = CssPropertyCacheBreakdown {
4207 cascaded_props_bytes: usize::MAX,
4208 ..Default::default()
4209 };
4210 assert_eq!(b.total_bytes(), usize::MAX);
4211 }
4212
4213 #[test]
4218 fn cache_empty_zero_is_neutral() {
4219 let c = CssPropertyCache::empty(0);
4220 assert_eq!(c.node_count, 0);
4221 assert!(c.css_props.is_empty());
4222 assert!(c.cascaded_props.is_empty());
4223 assert!(c.computed_values.is_empty());
4224 assert!(c.user_overridden_properties.is_empty());
4225 assert!(c.global_css_props.is_empty());
4226 assert!(c.compact_cache.is_none());
4227
4228 let b = c.memory_breakdown();
4229 assert_eq!(b.node_count, 0);
4230 assert_eq!(b.total_bytes(), 0, "a zero-node cache retains no heap");
4231 }
4232
4233 #[test]
4234 fn cache_empty_invariants_hold() {
4235 let c = CssPropertyCache::empty(7);
4236 assert_eq!(c.node_count, 7);
4237 assert_eq!(c.css_props.len(), 7);
4238 assert_eq!(c.cascaded_props.len(), 7);
4239 assert!(!c.css_props.is_flattened(), "starts in build phase");
4240 assert!(c.compact_cache.is_none());
4241
4242 let b = c.memory_breakdown();
4243 assert_eq!(b.node_count, 7);
4244 assert!(b.total_bytes() > 0);
4245 assert_eq!(b.compact_cache_bytes, 0);
4246 assert_eq!(b.resolved_font_sizes_bytes, 0);
4247 }
4248
4249 #[test]
4250 fn cache_invalidate_resolved_font_sizes_clears_the_once_lock() {
4251 let mut c = CssPropertyCache::empty(1);
4252 assert!(c.resolved_font_sizes_px.set(vec![16.0]).is_ok());
4253 assert!(c.resolved_font_sizes_px.get().is_some());
4254
4255 c.invalidate_resolved_font_sizes();
4256 assert!(
4257 c.resolved_font_sizes_px.get().is_none(),
4258 "next read must recompute"
4259 );
4260 assert!(c.resolved_font_sizes_px.set(vec![12.0]).is_ok());
4262 }
4263
4264 #[test]
4265 fn cache_append_sums_nodes_and_invalidates_derived_caches() {
4266 let mut a = CssPropertyCache::empty(2);
4267 let mut b = CssPropertyCache::empty(3);
4268 assert!(a.resolved_font_sizes_px.set(vec![16.0, 16.0]).is_ok());
4269
4270 a.append(&mut b);
4271
4272 assert_eq!(a.node_count, 5);
4273 assert_eq!(a.css_props.len(), 5);
4274 assert_eq!(a.cascaded_props.len(), 5);
4275 assert!(
4276 a.resolved_font_sizes_px.get().is_none(),
4277 "node indices shifted"
4278 );
4279 assert!(a.compact_cache.is_none());
4280 }
4281
4282 #[test]
4283 fn cache_append_of_empty_cache_is_a_noop_on_node_count() {
4284 let mut a = CssPropertyCache::empty(2);
4285 let mut b = CssPropertyCache::empty(0);
4286 a.append(&mut b);
4287 assert_eq!(a.node_count, 2);
4288 assert_eq!(a.css_props.len(), 2);
4289 }
4290
4291 #[test]
4292 fn cache_invalidate_resolved_cache_drops_compact_cache() {
4293 let mut c = CssPropertyCache::empty(1);
4294 c.invalidate_resolved_cache();
4295 assert!(c.compact_cache.is_none());
4296 }
4297
4298 #[test]
4299 fn cache_ptr_new_and_downcast_roundtrip() {
4300 let mut p = CssPropertyCachePtr::new(CssPropertyCache::empty(4));
4301 assert!(p.run_destructor);
4302 assert_eq!(p.downcast_mut().node_count, 4);
4303
4304 p.downcast_mut().node_count = 9;
4305 assert_eq!(p.downcast_mut().node_count, 9, "downcast_mut aliases the box");
4306 }
4307
4308 #[test]
4313 fn overflow_predicates_default_to_visible_for_a_bare_div() {
4314 let c = CssPropertyCache::empty(1);
4315 let nd = NodeData::create_div();
4316 assert!(c.is_horizontal_overflow_visible(&nd, &n0(), &normal()));
4317 assert!(c.is_vertical_overflow_visible(&nd, &n0(), &normal()));
4318 assert!(!c.is_horizontal_overflow_hidden(&nd, &n0(), &normal()));
4319 assert!(!c.is_vertical_overflow_hidden(&nd, &n0(), &normal()));
4320 }
4321
4322 #[test]
4323 fn overflow_predicates_are_per_axis() {
4324 let c = CssPropertyCache::empty(1);
4325 let nd = div_with(vec![CssProperty::OverflowX(CssPropertyValue::Exact(
4326 LayoutOverflow::Hidden,
4327 ))]);
4328 assert!(c.is_horizontal_overflow_hidden(&nd, &n0(), &normal()));
4329 assert!(!c.is_horizontal_overflow_visible(&nd, &n0(), &normal()));
4330 assert!(!c.is_vertical_overflow_hidden(&nd, &n0(), &normal()));
4332 assert!(c.is_vertical_overflow_visible(&nd, &n0(), &normal()));
4333 }
4334
4335 #[test]
4336 fn overflow_predicates_do_not_panic_on_an_out_of_range_node_id() {
4337 let c = CssPropertyCache::empty(0);
4338 let nd = NodeData::create_div();
4339 let far = NodeId::new(999_999);
4340 assert!(c.is_horizontal_overflow_visible(&nd, &far, &normal()));
4341 assert!(!c.is_vertical_overflow_hidden(&nd, &far, &normal()));
4342 }
4343
4344 #[test]
4345 fn has_border_false_without_and_true_with_a_border_width() {
4346 let c = CssPropertyCache::empty(1);
4347 assert!(!c.has_border(&NodeData::create_div(), &n0(), &normal()));
4348
4349 let bordered = div_with(vec![CssProperty::BorderLeftWidth(CssPropertyValue::Exact(
4350 LayoutBorderLeftWidth {
4351 inner: PixelValue::px(2.0),
4352 },
4353 ))]);
4354 assert!(c.has_border(&bordered, &n0(), &normal()));
4355 }
4356
4357 #[test]
4358 fn has_box_shadow_false_for_a_bare_div() {
4359 let c = CssPropertyCache::empty(1);
4360 assert!(!c.has_box_shadow(&NodeData::create_div(), &n0(), &normal()));
4361 assert!(!c.has_box_shadow(&NodeData::create_div(), &NodeId::new(500), &normal()));
4363 }
4364
4365 #[test]
4370 fn or_default_getters_fall_back_to_the_css_defaults() {
4371 let c = CssPropertyCache::empty(1);
4372 let nd = NodeData::create_div();
4373
4374 assert_eq!(
4375 c.get_font_size_or_default(&nd, &n0(), &normal()),
4376 azul_css::defaults::DEFAULT_FONT_SIZE
4377 );
4378 assert_eq!(
4379 c.get_text_color_or_default(&nd, &n0(), &normal()),
4380 azul_css::defaults::DEFAULT_TEXT_COLOR
4381 );
4382
4383 let fams = c.get_font_id_or_default(&nd, &n0(), &normal());
4384 assert_eq!(fams.as_ref().len(), 1);
4385 match &fams.as_ref()[0] {
4386 StyleFontFamily::System(s) => {
4387 assert_eq!(s.as_str(), azul_css::defaults::DEFAULT_FONT_ID);
4388 }
4389 other => panic!("expected the default System font family, got {other:?}"),
4390 }
4391 }
4392
4393 #[test]
4394 fn get_font_size_or_default_prefers_the_inline_value() {
4395 let c = CssPropertyCache::empty(1);
4396 let nd = div_with(vec![font_size(PixelValue::px(42.0))]);
4397 let fs = c.get_font_size_or_default(&nd, &n0(), &normal());
4398 assert!(close(fs.inner.number.get(), 42.0));
4399 assert_eq!(fs.inner.metric, SizeMetric::Px);
4400 }
4401
4402 #[test]
4403 fn or_default_getters_survive_an_out_of_range_node_id() {
4404 let c = CssPropertyCache::empty(0);
4405 let nd = NodeData::create_div();
4406 let far = NodeId::new(usize::MAX / 2);
4407 assert_eq!(
4408 c.get_font_size_or_default(&nd, &far, &normal()),
4409 azul_css::defaults::DEFAULT_FONT_SIZE
4410 );
4411 assert_eq!(c.get_font_id_or_default(&nd, &far, &normal()).as_ref().len(), 1);
4412 }
4413
4414 #[test]
4419 fn calc_width_is_zero_when_unset() {
4420 let c = CssPropertyCache::empty(1);
4421 let nd = NodeData::create_div();
4422 assert_eq!(c.calc_width(&nd, &n0(), &normal(), 800.0), 0.0);
4423 assert_eq!(c.calc_width(&nd, &n0(), &normal(), 0.0), 0.0);
4424 assert_eq!(c.calc_height(&nd, &n0(), &normal(), f32::NAN), 0.0);
4425 }
4426
4427 #[test]
4428 fn calc_width_resolves_px_and_percent() {
4429 let c = CssPropertyCache::empty(1);
4430
4431 let px = div_with(vec![width_px(100.0)]);
4432 assert!(close(c.calc_width(&px, &n0(), &normal(), 800.0), 100.0));
4433 assert!(close(c.calc_width(&px, &n0(), &normal(), 0.0), 100.0));
4435
4436 let pct = div_with(vec![width_pct(50.0)]);
4437 assert!(close(c.calc_width(&pct, &n0(), &normal(), 800.0), 400.0));
4438 assert!(close(c.calc_width(&pct, &n0(), &normal(), 0.0), 0.0));
4439 }
4440
4441 #[test]
4442 fn calc_width_with_a_negative_reference_is_negative_not_clamped() {
4443 let c = CssPropertyCache::empty(1);
4444 let pct = div_with(vec![width_pct(50.0)]);
4445 assert!(close(c.calc_width(&pct, &n0(), &normal(), -800.0), -400.0));
4446 }
4447
4448 #[test]
4449 fn calc_width_with_nan_and_infinite_references_is_defined() {
4450 let c = CssPropertyCache::empty(1);
4451 let pct = div_with(vec![width_pct(50.0)]);
4452
4453 assert!(c.calc_width(&pct, &n0(), &normal(), f32::NAN).is_nan());
4454 assert_eq!(
4455 c.calc_width(&pct, &n0(), &normal(), f32::INFINITY),
4456 f32::INFINITY
4457 );
4458 assert_eq!(
4459 c.calc_width(&pct, &n0(), &normal(), f32::NEG_INFINITY),
4460 f32::NEG_INFINITY
4461 );
4462 }
4463
4464 #[test]
4465 fn calc_width_saturates_non_finite_pixel_values_at_construction() {
4466 let c = CssPropertyCache::empty(1);
4467
4468 let nan = div_with(vec![width_px(f32::NAN)]);
4472 assert_eq!(c.calc_width(&nan, &n0(), &normal(), 800.0), 0.0);
4473
4474 let inf = div_with(vec![width_px(f32::INFINITY)]);
4475 let got = c.calc_width(&inf, &n0(), &normal(), 800.0);
4476 assert!(got.is_finite() && got > 0.0, "saturated, got {got}");
4477
4478 let neg_inf = div_with(vec![width_px(f32::NEG_INFINITY)]);
4479 let got = c.calc_width(&neg_inf, &n0(), &normal(), 800.0);
4480 assert!(got.is_finite() && got < 0.0, "saturated, got {got}");
4481
4482 let huge = div_with(vec![width_px(f32::MAX)]);
4483 assert!(c.calc_width(&huge, &n0(), &normal(), 800.0).is_finite());
4484 }
4485
4486 #[test]
4487 fn calc_width_of_auto_and_intrinsic_keywords_is_zero() {
4488 let c = CssPropertyCache::empty(1);
4489
4490 let auto = div_with(vec![CssProperty::Width(CssPropertyValue::Auto)]);
4491 assert_eq!(c.calc_width(&auto, &n0(), &normal(), 800.0), 0.0);
4492
4493 let min_content = div_with(vec![CssProperty::Width(CssPropertyValue::Exact(
4495 LayoutWidth::MinContent,
4496 ))]);
4497 assert_eq!(c.calc_width(&min_content, &n0(), &normal(), 800.0), 0.0);
4498 }
4499
4500 #[test]
4501 fn calc_height_mirrors_calc_width() {
4502 let c = CssPropertyCache::empty(1);
4503 let nd = div_with(vec![CssProperty::Height(CssPropertyValue::Exact(
4504 LayoutHeight::Px(PixelValue::percent(25.0)),
4505 ))]);
4506 assert!(close(c.calc_height(&nd, &n0(), &normal(), 400.0), 100.0));
4507 assert!(c.calc_height(&nd, &n0(), &normal(), f32::NAN).is_nan());
4508 }
4509
4510 #[test]
4511 fn calc_min_width_defaults_to_zero_and_max_width_defaults_to_none() {
4512 let c = CssPropertyCache::empty(1);
4513 let nd = NodeData::create_div();
4514
4515 assert_eq!(c.calc_min_width(&nd, &n0(), &normal(), 800.0), 0.0);
4516 assert_eq!(c.calc_min_height(&nd, &n0(), &normal(), 600.0), 0.0);
4517 assert_eq!(c.calc_max_width(&nd, &n0(), &normal(), 800.0), None);
4518 assert_eq!(c.calc_max_height(&nd, &n0(), &normal(), 600.0), None);
4519 }
4520
4521 #[test]
4522 fn calc_min_max_width_resolve_percentages_and_propagate_nan() {
4523 let c = CssPropertyCache::empty(1);
4524 let nd = div_with(vec![
4525 CssProperty::MinWidth(CssPropertyValue::Exact(LayoutMinWidth {
4526 inner: PixelValue::percent(10.0),
4527 })),
4528 CssProperty::MaxWidth(CssPropertyValue::Exact(LayoutMaxWidth {
4529 inner: PixelValue::percent(90.0),
4530 })),
4531 ]);
4532
4533 assert!(close(c.calc_min_width(&nd, &n0(), &normal(), 1000.0), 100.0));
4534 assert!(close(
4535 c.calc_max_width(&nd, &n0(), &normal(), 1000.0).unwrap(),
4536 900.0
4537 ));
4538 assert!(c.calc_min_width(&nd, &n0(), &normal(), f32::NAN).is_nan());
4539 assert!(c
4540 .calc_max_width(&nd, &n0(), &normal(), f32::NAN)
4541 .unwrap()
4542 .is_nan());
4543 }
4544
4545 #[test]
4546 fn calc_inset_getters_are_none_when_unset_and_some_when_set() {
4547 let c = CssPropertyCache::empty(1);
4548 let bare = NodeData::create_div();
4549 assert_eq!(c.calc_left(&bare, &n0(), &normal(), 800.0), None);
4550 assert_eq!(c.calc_right(&bare, &n0(), &normal(), 800.0), None);
4551 assert_eq!(c.calc_top(&bare, &n0(), &normal(), 600.0), None);
4552 assert_eq!(c.calc_bottom(&bare, &n0(), &normal(), 600.0), None);
4553
4554 let inset = div_with(vec![
4555 CssProperty::Left(CssPropertyValue::Exact(LayoutLeft {
4556 inner: PixelValue::px(5.0),
4557 })),
4558 CssProperty::Right(CssPropertyValue::Exact(LayoutRight {
4559 inner: PixelValue::percent(10.0),
4560 })),
4561 CssProperty::Top(CssPropertyValue::Exact(LayoutTop {
4562 inner: PixelValue::px(-7.0),
4563 })),
4564 CssProperty::Bottom(CssPropertyValue::Exact(LayoutInsetBottom {
4565 inner: PixelValue::px(0.0),
4566 })),
4567 ]);
4568 assert!(close(c.calc_left(&inset, &n0(), &normal(), 800.0).unwrap(), 5.0));
4569 assert!(close(
4570 c.calc_right(&inset, &n0(), &normal(), 800.0).unwrap(),
4571 80.0
4572 ));
4573 assert!(close(
4574 c.calc_top(&inset, &n0(), &normal(), 600.0).unwrap(),
4575 -7.0
4576 ));
4577 assert_eq!(c.calc_bottom(&inset, &n0(), &normal(), 600.0), Some(0.0));
4578 }
4579
4580 #[test]
4581 fn calc_padding_margin_border_default_to_zero() {
4582 let c = CssPropertyCache::empty(1);
4583 let nd = NodeData::create_div();
4584 assert_eq!(c.calc_padding_left(&nd, &n0(), &normal(), 800.0), 0.0);
4585 assert_eq!(c.calc_padding_right(&nd, &n0(), &normal(), 800.0), 0.0);
4586 assert_eq!(c.calc_padding_top(&nd, &n0(), &normal(), 600.0), 0.0);
4587 assert_eq!(c.calc_padding_bottom(&nd, &n0(), &normal(), 600.0), 0.0);
4588 assert_eq!(c.calc_margin_left(&nd, &n0(), &normal(), 800.0), 0.0);
4589 assert_eq!(c.calc_margin_right(&nd, &n0(), &normal(), 800.0), 0.0);
4590 assert_eq!(c.calc_margin_top(&nd, &n0(), &normal(), 600.0), 0.0);
4591 assert_eq!(c.calc_margin_bottom(&nd, &n0(), &normal(), 600.0), 0.0);
4592 assert_eq!(c.calc_border_left_width(&nd, &n0(), &normal(), 800.0), 0.0);
4593 assert_eq!(c.calc_border_right_width(&nd, &n0(), &normal(), 800.0), 0.0);
4594 assert_eq!(c.calc_border_top_width(&nd, &n0(), &normal(), 600.0), 0.0);
4595 assert_eq!(c.calc_border_bottom_width(&nd, &n0(), &normal(), 600.0), 0.0);
4596 }
4597
4598 #[test]
4599 fn calc_padding_em_uses_the_default_font_size_not_the_reference() {
4600 let c = CssPropertyCache::empty(1);
4603 let nd = div_with(vec![CssProperty::PaddingLeft(CssPropertyValue::Exact(
4604 LayoutPaddingLeft {
4605 inner: PixelValue::em(2.0),
4606 },
4607 ))]);
4608 assert!(close(c.calc_padding_left(&nd, &n0(), &normal(), 800.0), 32.0));
4609 assert!(close(c.calc_padding_left(&nd, &n0(), &normal(), 0.0), 32.0));
4610 assert!(close(
4611 c.calc_padding_left(&nd, &n0(), &normal(), f32::NAN),
4612 32.0
4613 ));
4614 }
4615
4616 #[test]
4617 fn calc_margin_and_border_resolve_px_and_percent() {
4618 let c = CssPropertyCache::empty(1);
4619 let nd = div_with(vec![
4620 CssProperty::MarginTop(CssPropertyValue::Exact(LayoutMarginTop {
4621 inner: PixelValue::percent(50.0),
4622 })),
4623 CssProperty::BorderLeftWidth(CssPropertyValue::Exact(LayoutBorderLeftWidth {
4624 inner: PixelValue::px(3.0),
4625 })),
4626 ]);
4627 assert!(close(c.calc_margin_top(&nd, &n0(), &normal(), 200.0), 100.0));
4628 assert!(close(
4629 c.calc_border_left_width(&nd, &n0(), &normal(), 800.0),
4630 3.0
4631 ));
4632 assert!(c.calc_margin_top(&nd, &n0(), &normal(), f32::NAN).is_nan());
4633 }
4634
4635 #[test]
4636 fn calc_getters_do_not_panic_on_an_out_of_range_node_id() {
4637 let c = CssPropertyCache::empty(0);
4638 let nd = NodeData::create_div();
4639 let far = NodeId::new(usize::MAX / 2);
4640 assert_eq!(c.calc_width(&nd, &far, &normal(), 800.0), 0.0);
4641 assert_eq!(c.calc_max_height(&nd, &far, &normal(), 600.0), None);
4642 assert_eq!(c.calc_padding_top(&nd, &far, &normal(), f32::INFINITY), 0.0);
4643 }
4644
4645 #[test]
4650 fn slow_path_only_needed_for_non_px_pixel_values() {
4651 assert!(!property_needs_slow_path_after_compact(&width_px(10.0)));
4653 assert!(property_needs_slow_path_after_compact(&width_pct(50.0)));
4655
4656 assert!(!property_needs_slow_path_after_compact(&CssProperty::Height(
4657 CssPropertyValue::Exact(LayoutHeight::Px(PixelValue::px(1.0)))
4658 )));
4659 assert!(property_needs_slow_path_after_compact(&CssProperty::Height(
4660 CssPropertyValue::Exact(LayoutHeight::Px(PixelValue::em(1.0)))
4661 )));
4662 }
4663
4664 #[test]
4665 fn slow_path_covers_the_plain_pixelvalue_wrappers() {
4666 assert!(property_needs_slow_path_after_compact(&font_size(
4667 PixelValue::rem(2.0)
4668 )));
4669 assert!(!property_needs_slow_path_after_compact(&font_size(
4670 PixelValue::px(16.0)
4671 )));
4672
4673 assert!(property_needs_slow_path_after_compact(
4674 &CssProperty::MinWidth(CssPropertyValue::Exact(LayoutMinWidth {
4675 inner: PixelValue::percent(10.0),
4676 }))
4677 ));
4678 assert!(!property_needs_slow_path_after_compact(
4679 &CssProperty::PaddingLeft(CssPropertyValue::Exact(LayoutPaddingLeft {
4680 inner: PixelValue::px(4.0),
4681 }))
4682 ));
4683 }
4684
4685 #[test]
4686 fn slow_path_handles_flex_basis_and_non_pixel_properties() {
4687 assert!(property_needs_slow_path_after_compact(
4688 &CssProperty::FlexBasis(CssPropertyValue::Exact(LayoutFlexBasis::Exact(
4689 PixelValue::percent(50.0)
4690 )))
4691 ));
4692 assert!(!property_needs_slow_path_after_compact(
4693 &CssProperty::FlexBasis(CssPropertyValue::Exact(LayoutFlexBasis::Auto))
4694 ));
4695
4696 assert!(!property_needs_slow_path_after_compact(&CssProperty::Width(
4698 CssPropertyValue::Auto
4699 )));
4700 assert!(!property_needs_slow_path_after_compact(
4701 &CssProperty::const_none(CssPropertyType::Display)
4702 ));
4703 assert!(!property_needs_slow_path_after_compact(
4704 &CssProperty::const_none(CssPropertyType::BackgroundContent)
4705 ));
4706 }
4707
4708 #[test]
4713 fn clone_inheritable_property_round_trips_heap_and_pod_variants() {
4714 let font_family = CssProperty::FontFamily(CssPropertyValue::Exact(
4717 vec![StyleFontFamily::System(AzString::from_const_str("serif"))].into(),
4718 ));
4719 assert_eq!(clone_inheritable_property(&font_family), font_family);
4720
4721 for p in [
4722 CssProperty::const_none(CssPropertyType::Cursor),
4723 CssProperty::const_none(CssPropertyType::TextColor),
4724 CssProperty::const_none(CssPropertyType::BackgroundContent),
4725 CssProperty::const_none(CssPropertyType::Transform),
4726 CssProperty::const_none(CssPropertyType::Content),
4727 width_px(3.0),
4728 font_size(PixelValue::em(1.5)),
4729 ] {
4730 assert_eq!(clone_inheritable_property(&p), p, "clone must be identity");
4731 assert_eq!(clone_inheritable_property(&p).get_type(), p.get_type());
4732 }
4733 }
4734
4735 fn sorted_stateful_fixture() -> Vec<StatefulCssProperty> {
4740 let mut v = vec![
4741 stateful(PseudoStateType::Normal, width_px(1.0)),
4742 stateful(
4743 PseudoStateType::Normal,
4744 CssProperty::const_none(CssPropertyType::Display),
4745 ),
4746 stateful(PseudoStateType::Hover, width_px(2.0)),
4747 ];
4748 v.sort_by_key(|p| (p.state, p.prop_type));
4750 v
4751 }
4752
4753 #[test]
4754 fn find_in_stateful_on_an_empty_slice_is_none() {
4755 assert!(CssPropertyCache::find_in_stateful(
4756 &[],
4757 PseudoStateType::Normal,
4758 &CssPropertyType::Width
4759 )
4760 .is_none());
4761 }
4762
4763 #[test]
4764 fn find_in_stateful_is_keyed_on_both_state_and_prop_type() {
4765 let v = sorted_stateful_fixture();
4766
4767 let normal_width =
4768 CssPropertyCache::find_in_stateful(&v, PseudoStateType::Normal, &CssPropertyType::Width)
4769 .expect("normal width present");
4770 assert_eq!(normal_width.get_type(), CssPropertyType::Width);
4771
4772 let hover_width =
4773 CssPropertyCache::find_in_stateful(&v, PseudoStateType::Hover, &CssPropertyType::Width)
4774 .expect("hover width present");
4775 assert_ne!(normal_width, hover_width);
4777
4778 assert!(CssPropertyCache::find_in_stateful(
4780 &v,
4781 PseudoStateType::Focus,
4782 &CssPropertyType::Width
4783 )
4784 .is_none());
4785 assert!(CssPropertyCache::find_in_stateful(
4787 &v,
4788 PseudoStateType::Hover,
4789 &CssPropertyType::Display
4790 )
4791 .is_none());
4792 }
4793
4794 #[test]
4795 fn has_state_props_true_false_and_edges() {
4796 let v = sorted_stateful_fixture();
4797 assert!(CssPropertyCache::has_state_props(&v, PseudoStateType::Normal));
4798 assert!(CssPropertyCache::has_state_props(&v, PseudoStateType::Hover));
4799 assert!(!CssPropertyCache::has_state_props(&v, PseudoStateType::Focus));
4800 assert!(!CssPropertyCache::has_state_props(
4802 &[],
4803 PseudoStateType::Normal
4804 ));
4805 }
4806
4807 #[test]
4808 fn prop_types_for_state_filters_by_state() {
4809 let v = sorted_stateful_fixture();
4810
4811 let mut normal: Vec<CssPropertyType> =
4812 CssPropertyCache::prop_types_for_state(&v, PseudoStateType::Normal)
4813 .copied()
4814 .collect();
4815 normal.sort_unstable();
4816 assert_eq!(normal.len(), 2);
4817 assert!(normal.contains(&CssPropertyType::Width));
4818 assert!(normal.contains(&CssPropertyType::Display));
4819
4820 let hover: Vec<CssPropertyType> =
4821 CssPropertyCache::prop_types_for_state(&v, PseudoStateType::Hover)
4822 .copied()
4823 .collect();
4824 assert_eq!(hover, vec![CssPropertyType::Width]);
4825
4826 assert_eq!(
4827 CssPropertyCache::prop_types_for_state(&v, PseudoStateType::Active).count(),
4828 0
4829 );
4830 assert_eq!(
4831 CssPropertyCache::prop_types_for_state(&[], PseudoStateType::Normal).count(),
4832 0
4833 );
4834 }
4835
4836 #[test]
4841 fn resolve_font_size_to_pixels_converts_absolute_units() {
4842 let px = CssPropertyCache::resolve_font_size_to_pixels(&font_size(PixelValue::px(20.0)), 10.0);
4843 let (metric, n) = font_size_parts(&px).unwrap();
4844 assert_eq!(metric, SizeMetric::Px);
4845 assert!(close(n, 20.0));
4846
4847 let pt = CssPropertyCache::resolve_font_size_to_pixels(&font_size(PixelValue::pt(12.0)), 10.0);
4848 assert!(close(font_size_parts(&pt).unwrap().1, 12.0 * PT_TO_PX));
4849 }
4850
4851 #[test]
4852 fn resolve_font_size_to_pixels_em_scales_by_reference_but_rem_does_not() {
4853 let em =
4854 CssPropertyCache::resolve_font_size_to_pixels(&font_size(PixelValue::em(2.0)), 10.0);
4855 assert!(close(font_size_parts(&em).unwrap().1, 20.0));
4856
4857 let rem =
4859 CssPropertyCache::resolve_font_size_to_pixels(&font_size(PixelValue::rem(2.0)), 10.0);
4860 assert!(close(font_size_parts(&rem).unwrap().1, 32.0));
4861
4862 let pct = CssPropertyCache::resolve_font_size_to_pixels(
4863 &font_size(PixelValue::percent(50.0)),
4864 10.0,
4865 );
4866 assert!(close(font_size_parts(&pct).unwrap().1, 5.0));
4867 }
4868
4869 #[test]
4870 fn resolve_font_size_to_pixels_with_nan_and_infinite_references() {
4871 let nan =
4873 CssPropertyCache::resolve_font_size_to_pixels(&font_size(PixelValue::em(2.0)), f32::NAN);
4874 let (metric, n) = font_size_parts(&nan).unwrap();
4875 assert_eq!(metric, SizeMetric::Px);
4876 assert_eq!(n, 0.0, "NaN must not escape into the cascade");
4877
4878 let inf = CssPropertyCache::resolve_font_size_to_pixels(
4879 &font_size(PixelValue::em(2.0)),
4880 f32::INFINITY,
4881 );
4882 let n = font_size_parts(&inf).unwrap().1;
4883 assert!(n.is_finite() && n > 0.0, "saturated, got {n}");
4884
4885 let zero =
4886 CssPropertyCache::resolve_font_size_to_pixels(&font_size(PixelValue::em(2.0)), 0.0);
4887 assert_eq!(font_size_parts(&zero).unwrap().1, 0.0);
4888
4889 let neg =
4890 CssPropertyCache::resolve_font_size_to_pixels(&font_size(PixelValue::em(2.0)), -10.0);
4891 assert!(close(font_size_parts(&neg).unwrap().1, -20.0));
4892 }
4893
4894 #[test]
4895 fn resolve_font_size_to_pixels_passes_through_unresolvable_inputs() {
4896 let vw = font_size(PixelValue::from_metric(SizeMetric::Vw, 10.0));
4898 assert_eq!(CssPropertyCache::resolve_font_size_to_pixels(&vw, 16.0), vw);
4899
4900 let w = width_px(10.0);
4902 assert_eq!(CssPropertyCache::resolve_font_size_to_pixels(&w, 16.0), w);
4903
4904 let inherit = CssProperty::FontSize(CssPropertyValue::Inherit);
4906 assert_eq!(
4907 CssPropertyCache::resolve_font_size_to_pixels(&inherit, 16.0),
4908 inherit
4909 );
4910 }
4911
4912 #[test]
4913 fn has_relative_font_size_unit_true_false_and_edges() {
4914 assert!(CssPropertyCache::has_relative_font_size_unit(&font_size(
4915 PixelValue::em(1.0)
4916 )));
4917 assert!(CssPropertyCache::has_relative_font_size_unit(&font_size(
4918 PixelValue::rem(1.0)
4919 )));
4920 assert!(CssPropertyCache::has_relative_font_size_unit(&font_size(
4921 PixelValue::percent(100.0)
4922 )));
4923
4924 assert!(!CssPropertyCache::has_relative_font_size_unit(&font_size(
4925 PixelValue::px(16.0)
4926 )));
4927 assert!(!CssPropertyCache::has_relative_font_size_unit(&font_size(
4928 PixelValue::pt(12.0)
4929 )));
4930 assert!(!CssPropertyCache::has_relative_font_size_unit(
4932 &CssProperty::FontSize(CssPropertyValue::Auto)
4933 ));
4934 assert!(!CssPropertyCache::has_relative_font_size_unit(&width_px(1.0)));
4935 }
4936
4937 #[test]
4942 fn resolve_property_dependency_scales_relative_targets_by_an_absolute_reference() {
4943 let reference = font_size(PixelValue::px(10.0));
4944
4945 let em = CssPropertyCache::resolve_property_dependency(
4946 &font_size(PixelValue::em(2.0)),
4947 &reference,
4948 )
4949 .expect("em resolves against an absolute reference");
4950 assert!(close(font_size_parts(&em).unwrap().1, 20.0));
4951
4952 let pct = CssPropertyCache::resolve_property_dependency(
4953 &font_size(PixelValue::percent(50.0)),
4954 &reference,
4955 )
4956 .expect("percent resolves");
4957 assert!(close(font_size_parts(&pct).unwrap().1, 5.0));
4958
4959 let pt_ref = font_size(PixelValue::pt(10.0));
4961 let em2 =
4962 CssPropertyCache::resolve_property_dependency(&font_size(PixelValue::em(2.0)), &pt_ref)
4963 .expect("pt reference is absolute");
4964 assert!(close(
4965 font_size_parts(&em2).unwrap().1,
4966 2.0 * 10.0 * PT_TO_PX
4967 ));
4968 }
4969
4970 #[test]
4971 fn resolve_property_dependency_rewrites_the_target_variant_in_place() {
4972 let reference = font_size(PixelValue::px(10.0));
4973 let padding = CssProperty::PaddingLeft(CssPropertyValue::Exact(LayoutPaddingLeft {
4974 inner: PixelValue::em(3.0),
4975 }));
4976 let out = CssPropertyCache::resolve_property_dependency(&padding, &reference)
4977 .expect("padding is a supported target");
4978 match out {
4979 CssProperty::PaddingLeft(v) => {
4980 let inner = v.get_property().unwrap().inner;
4981 assert_eq!(inner.metric, SizeMetric::Px);
4982 assert!(close(inner.number.get(), 30.0));
4983 }
4984 other => panic!("variant must be preserved, got {other:?}"),
4985 }
4986 }
4987
4988 #[test]
4989 fn resolve_property_dependency_returns_none_for_unresolvable_inputs() {
4990 let abs = font_size(PixelValue::px(10.0));
4991
4992 assert!(CssPropertyCache::resolve_property_dependency(
4994 &font_size(PixelValue::em(2.0)),
4995 &font_size(PixelValue::em(2.0))
4996 )
4997 .is_none());
4998 assert!(CssPropertyCache::resolve_property_dependency(
5000 &font_size(PixelValue::from_metric(SizeMetric::Vh, 5.0)),
5001 &abs
5002 )
5003 .is_none());
5004 assert!(CssPropertyCache::resolve_property_dependency(&width_px(5.0), &abs).is_none());
5006 assert!(CssPropertyCache::resolve_property_dependency(
5008 &font_size(PixelValue::em(2.0)),
5009 &width_px(5.0)
5010 )
5011 .is_none());
5012 assert!(CssPropertyCache::resolve_property_dependency(
5014 &CssProperty::FontSize(CssPropertyValue::Inherit),
5015 &abs
5016 )
5017 .is_none());
5018 }
5019
5020 #[test]
5025 fn should_apply_cascaded_respects_origin_and_relative_font_sizes() {
5026 let own = |p: CssProperty| {
5027 vec![(
5028 p.get_type(),
5029 CssPropertyWithOrigin {
5030 property: p,
5031 origin: CssPropertyOrigin::Own,
5032 },
5033 )]
5034 };
5035 let inherited = |p: CssProperty| {
5036 vec![(
5037 p.get_type(),
5038 CssPropertyWithOrigin {
5039 property: p,
5040 origin: CssPropertyOrigin::Inherited,
5041 },
5042 )]
5043 };
5044
5045 assert!(CssPropertyCache::should_apply_cascaded(
5047 &[],
5048 CssPropertyType::Width,
5049 &width_px(1.0)
5050 ));
5051
5052 assert!(!CssPropertyCache::should_apply_cascaded(
5054 &own(width_px(2.0)),
5055 CssPropertyType::Width,
5056 &width_px(1.0)
5057 ));
5058
5059 assert!(CssPropertyCache::should_apply_cascaded(
5061 &inherited(width_px(2.0)),
5062 CssPropertyType::Width,
5063 &width_px(1.0)
5064 ));
5065
5066 let inherited_fs = inherited(font_size(PixelValue::px(20.0)));
5071 assert!(CssPropertyCache::should_apply_cascaded(
5072 &inherited_fs,
5073 CssPropertyType::FontSize,
5074 &font_size(PixelValue::em(2.0))
5075 ));
5076 assert!(CssPropertyCache::should_apply_cascaded(
5077 &inherited_fs,
5078 CssPropertyType::FontSize,
5079 &font_size(PixelValue::px(12.0))
5080 ));
5081 }
5082
5083 #[test]
5088 fn get_property_finds_an_inline_normal_property() {
5089 let c = CssPropertyCache::empty(1);
5090 let nd = div_with(vec![width_px(100.0)]);
5091 let got = c
5092 .get_property(&nd, &n0(), &normal(), &CssPropertyType::Width)
5093 .expect("inline width");
5094 assert_eq!(*got, width_px(100.0));
5095 }
5096
5097 #[test]
5098 fn get_property_ignores_pseudo_state_props_unless_the_state_is_active() {
5099 let c = CssPropertyCache::empty(1);
5100 let nd = div_with_pseudo(vec![width_px(100.0)], PseudoStateType::Hover);
5101
5102 assert!(
5103 c.get_property(&nd, &n0(), &normal(), &CssPropertyType::Width)
5104 .is_none(),
5105 ":hover width must not leak into the Normal state"
5106 );
5107
5108 let hovered = StyledNodeState {
5109 hover: true,
5110 ..StyledNodeState::default()
5111 };
5112 assert_eq!(
5113 c.get_property(&nd, &n0(), &hovered, &CssPropertyType::Width),
5114 Some(&width_px(100.0))
5115 );
5116 }
5117
5118 #[test]
5119 fn get_property_user_override_beats_inline_and_stylesheet() {
5120 let mut c = CssPropertyCache::empty(1);
5121 c.user_overridden_properties
5122 .push(vec![(CssPropertyType::Width, width_px(1.0))]);
5123 c.css_props
5124 .push_to(0, stateful(PseudoStateType::Normal, width_px(2.0)));
5125 c.css_props.sort_each_and_flatten(|p| (p.state, p.prop_type));
5126
5127 let nd = div_with(vec![width_px(3.0)]);
5128 assert_eq!(
5129 c.get_property(&nd, &n0(), &normal(), &CssPropertyType::Width),
5130 Some(&width_px(1.0)),
5131 "user override is the top cascade layer"
5132 );
5133 }
5134
5135 #[test]
5136 fn get_property_falls_back_through_stylesheet_global_cascaded_then_ua() {
5137 let nd = NodeData::create_div();
5138
5139 let mut c = CssPropertyCache::empty(1);
5141 c.css_props
5142 .push_to(0, stateful(PseudoStateType::Normal, width_px(2.0)));
5143 c.css_props.sort_each_and_flatten(|p| (p.state, p.prop_type));
5144 assert_eq!(
5145 c.get_property(&nd, &n0(), &normal(), &CssPropertyType::Width),
5146 Some(&width_px(2.0))
5147 );
5148
5149 let mut c = CssPropertyCache::empty(1);
5151 c.global_css_props.push(width_px(4.0));
5152 assert_eq!(
5153 c.get_property(&nd, &n0(), &normal(), &CssPropertyType::Width),
5154 Some(&width_px(4.0))
5155 );
5156
5157 let mut c = CssPropertyCache::empty(1);
5159 c.cascaded_props
5160 .push_to(0, stateful(PseudoStateType::Normal, width_px(5.0)));
5161 c.cascaded_props
5162 .sort_each_and_flatten(|p| (p.state, p.prop_type));
5163 assert_eq!(
5164 c.get_property(&nd, &n0(), &normal(), &CssPropertyType::Width),
5165 Some(&width_px(5.0))
5166 );
5167
5168 let c = CssPropertyCache::empty(1);
5170 assert!(c
5171 .get_property(&nd, &n0(), &normal(), &CssPropertyType::Width)
5172 .is_none());
5173 assert!(c
5174 .get_property(&nd, &n0(), &normal(), &CssPropertyType::Display)
5175 .is_some());
5176 }
5177
5178 #[test]
5179 fn get_property_on_an_out_of_range_node_id_falls_through_to_ua_css() {
5180 let c = CssPropertyCache::empty(0);
5181 let nd = NodeData::create_div();
5182 let far = NodeId::new(usize::MAX / 2);
5183
5184 assert!(c
5185 .get_property(&nd, &far, &normal(), &CssPropertyType::Width)
5186 .is_none());
5187 assert!(
5188 c.get_property(&nd, &far, &normal(), &CssPropertyType::Display)
5189 .is_some(),
5190 "UA CSS is node-type-keyed, not index-keyed"
5191 );
5192 }
5193
5194 #[test]
5195 fn get_property_with_context_matches_pseudo_state_conditions() {
5196 let c = CssPropertyCache::empty(1);
5197 let nd = div_with_pseudo(vec![width_px(100.0)], PseudoStateType::Hover);
5198
5199 let plain = DynamicSelectorContext::default();
5200 assert!(c
5201 .get_property_with_context(&nd, &n0(), &plain, &CssPropertyType::Width)
5202 .is_none());
5203
5204 let mut hovered = DynamicSelectorContext::default();
5205 hovered.pseudo_state.hover = true;
5206 assert_eq!(
5207 c.get_property_with_context(&nd, &n0(), &hovered, &CssPropertyType::Width),
5208 Some(&width_px(100.0))
5209 );
5210 }
5211
5212 #[test]
5213 fn check_properties_changed_only_fires_when_a_condition_flips() {
5214 let plain = DynamicSelectorContext::default();
5215 let mut hovered = DynamicSelectorContext::default();
5216 hovered.pseudo_state.hover = true;
5217
5218 let unconditional = div_with(vec![width_px(1.0)]);
5220 assert!(!CssPropertyCache::check_properties_changed(
5221 &unconditional,
5222 &plain,
5223 &hovered
5224 ));
5225
5226 let conditional = div_with_pseudo(vec![width_px(1.0)], PseudoStateType::Hover);
5227 assert!(CssPropertyCache::check_properties_changed(
5228 &conditional,
5229 &plain,
5230 &hovered
5231 ));
5232 assert!(
5233 !CssPropertyCache::check_properties_changed(&conditional, &plain, &plain),
5234 "identical contexts can never differ"
5235 );
5236
5237 assert!(!CssPropertyCache::check_properties_changed(
5239 &NodeData::create_div(),
5240 &plain,
5241 &hovered
5242 ));
5243 }
5244
5245 #[test]
5246 fn check_layout_properties_changed_ignores_non_layout_properties() {
5247 let plain = DynamicSelectorContext::default();
5248 let mut hovered = DynamicSelectorContext::default();
5249 hovered.pseudo_state.hover = true;
5250
5251 let layout = div_with_pseudo(vec![width_px(1.0)], PseudoStateType::Hover);
5252 assert!(CssPropertyCache::check_layout_properties_changed(
5253 &layout, &plain, &hovered
5254 ));
5255 assert!(CssPropertyType::Width.can_trigger_relayout());
5256
5257 let paint = div_with_pseudo(
5259 vec![CssProperty::const_none(CssPropertyType::BackgroundContent)],
5260 PseudoStateType::Hover,
5261 );
5262 assert!(!CssPropertyType::BackgroundContent.can_trigger_relayout());
5263 assert!(!CssPropertyCache::check_layout_properties_changed(
5264 &paint, &plain, &hovered
5265 ));
5266 assert!(CssPropertyCache::check_properties_changed(
5268 &paint, &plain, &hovered
5269 ));
5270 }
5271
5272 #[test]
5277 fn grid_gap_and_scrollbar_getters_are_none_on_a_bare_div() {
5278 let c = CssPropertyCache::empty(1);
5279 let nd = NodeData::create_div();
5280 assert!(c.get_grid_gap(&nd, &n0(), &normal()).is_none());
5281 assert!(c.get_scrollbar_track(&nd, &n0(), &normal()).is_none());
5282 assert!(c.get_scrollbar_thumb(&nd, &n0(), &normal()).is_none());
5283 assert!(c.get_scrollbar_button(&nd, &n0(), &normal()).is_none());
5284 assert!(c.get_scrollbar_corner(&nd, &n0(), &normal()).is_none());
5285 assert!(c.get_scrollbar_resizer(&nd, &n0(), &normal()).is_none());
5286
5287 let far = NodeId::new(4_242);
5289 assert!(c.get_grid_gap(&nd, &far, &normal()).is_none());
5290 assert!(c.get_scrollbar_thumb(&nd, &far, &normal()).is_none());
5291 }
5292
5293 #[test]
5298 fn computed_css_style_string_serializes_set_properties() {
5299 let c = CssPropertyCache::empty(1);
5300
5301 let s = c.get_computed_css_style_string(&NodeData::create_div(), &n0(), &normal());
5303 assert!(s.contains("display:"), "got {s:?}");
5304
5305 let styled = div_with(vec![width_px(100.0), font_size(PixelValue::px(12.0))]);
5306 let s = c.get_computed_css_style_string(&styled, &n0(), &normal());
5307 assert!(s.contains("width:"), "got {s:?}");
5308 assert!(s.contains("font-size:"), "got {s:?}");
5309 assert!(s.ends_with(';'), "each declaration is terminated: {s:?}");
5310 }
5311
5312 #[test]
5313 fn computed_css_style_string_does_not_panic_on_an_out_of_range_node_id() {
5314 let c = CssPropertyCache::empty(0);
5315 let s = c.get_computed_css_style_string(
5316 &NodeData::create_div(),
5317 &NodeId::new(usize::MAX / 2),
5318 &normal(),
5319 );
5320 assert!(s.contains("display:"));
5321 }
5322
5323 #[test]
5328 fn apply_ua_css_inserts_ua_properties_into_cascaded_props() {
5329 let nodes = vec![NodeData::create_div()];
5330 let mut c = CssPropertyCache::empty(1);
5331 c.apply_ua_css(&nodes);
5332
5333 let props = c.cascaded_props.build_get(0).expect("build phase");
5334 assert!(
5335 props
5336 .iter()
5337 .any(|p| p.prop_type == CssPropertyType::Display
5338 && p.state == PseudoStateType::Normal),
5339 "UA `div {{ display: block }}` must land in the cascade"
5340 );
5341 }
5342
5343 #[test]
5344 fn apply_ua_css_does_not_override_an_existing_inline_property() {
5345 let nodes = vec![div_with(vec![CssProperty::const_none(
5346 CssPropertyType::Display,
5347 )])];
5348 let mut c = CssPropertyCache::empty(1);
5349 c.apply_ua_css(&nodes);
5350
5351 let props = c.cascaded_props.build_get(0).expect("build phase");
5352 assert!(
5353 !props.iter().any(|p| p.prop_type == CssPropertyType::Display),
5354 "UA CSS is the weakest layer and must not clobber inline"
5355 );
5356 }
5357
5358 #[test]
5359 fn apply_ua_css_on_zero_nodes_returns_early() {
5360 let mut c = CssPropertyCache::empty(0);
5361 c.apply_ua_css(&[]);
5362 assert_eq!(c.cascaded_props.len(), 0);
5363 }
5364
5365 #[test]
5366 fn sort_cascaded_props_flattens_and_orders_by_state_then_type() {
5367 let mut c = CssPropertyCache::empty(1);
5368 c.cascaded_props
5369 .push_to(0, stateful(PseudoStateType::Hover, width_px(1.0)));
5370 c.cascaded_props.push_to(
5371 0,
5372 stateful(
5373 PseudoStateType::Normal,
5374 CssProperty::const_none(CssPropertyType::Display),
5375 ),
5376 );
5377 c.cascaded_props
5378 .push_to(0, stateful(PseudoStateType::Normal, width_px(2.0)));
5379
5380 c.sort_cascaded_props();
5381
5382 assert!(c.cascaded_props.is_flattened());
5383 let slice = c.cascaded_props.get_slice(0);
5384 assert_eq!(slice.len(), 3);
5385 let keys: Vec<_> = slice.iter().map(|p| (p.state, p.prop_type)).collect();
5386 let mut sorted = keys.clone();
5387 sorted.sort_unstable();
5388 assert_eq!(keys, sorted, "binary_search lookups require sort order");
5389 }
5390
5391 #[test]
5392 fn prune_compact_normal_props_keeps_what_the_slow_path_still_needs() {
5393 let mut c = CssPropertyCache::empty(1);
5394 c.cascaded_props.push_to(
5396 0,
5397 stateful(
5398 PseudoStateType::Normal,
5399 CssProperty::const_none(CssPropertyType::Display),
5400 ),
5401 );
5402 c.cascaded_props
5404 .push_to(0, stateful(PseudoStateType::Normal, width_pct(50.0)));
5405 c.cascaded_props.push_to(
5407 0,
5408 stateful(
5409 PseudoStateType::Normal,
5410 CssProperty::const_none(CssPropertyType::BackgroundContent),
5411 ),
5412 );
5413 c.cascaded_props.push_to(
5415 0,
5416 stateful(
5417 PseudoStateType::Hover,
5418 CssProperty::const_none(CssPropertyType::Display),
5419 ),
5420 );
5421
5422 c.prune_compact_normal_props();
5423
5424 let kept: Vec<(PseudoStateType, CssPropertyType)> = c
5425 .cascaded_props
5426 .get_slice(0)
5427 .iter()
5428 .map(|p| (p.state, p.prop_type))
5429 .collect();
5430
5431 assert!(
5432 !kept.contains(&(PseudoStateType::Normal, CssPropertyType::Display)),
5433 "the compact cache is authoritative for this one"
5434 );
5435 assert!(kept.contains(&(PseudoStateType::Normal, CssPropertyType::Width)));
5436 assert!(kept.contains(&(PseudoStateType::Normal, CssPropertyType::BackgroundContent)));
5437 assert!(kept.contains(&(PseudoStateType::Hover, CssPropertyType::Display)));
5438 assert_eq!(kept.len(), 3);
5439 }
5440
5441 #[test]
5442 fn prune_compact_normal_props_on_an_empty_cache_does_not_panic() {
5443 let mut c = CssPropertyCache::empty(0);
5444 c.prune_compact_normal_props();
5445 assert_eq!(c.cascaded_props.len(), 0);
5446
5447 let mut c = CssPropertyCache::empty(3);
5448 c.prune_compact_normal_props();
5449 assert_eq!(c.cascaded_props.len(), 3);
5450 assert!(c.cascaded_props.get_slice(0).is_empty());
5451 }
5452
5453 fn two_node_hierarchy() -> Vec<NodeHierarchyItem> {
5459 vec![
5460 NodeHierarchyItem {
5461 parent: 0,
5462 previous_sibling: 0,
5463 next_sibling: 0,
5464 last_child: 2,
5465 },
5466 NodeHierarchyItem {
5467 parent: 1,
5468 previous_sibling: 0,
5469 next_sibling: 0,
5470 last_child: 0,
5471 },
5472 ]
5473 }
5474
5475 #[test]
5476 fn compute_inherited_values_propagates_font_size_to_children() {
5477 let hierarchy = two_node_hierarchy();
5478 assert_eq!(hierarchy[1].parent_id(), Some(NodeId::new(0)));
5479
5480 let nodes = vec![
5481 div_with(vec![font_size(PixelValue::px(20.0))]),
5482 NodeData::create_div(),
5483 ];
5484 let mut c = CssPropertyCache::empty(2);
5485 let changed = c.compute_inherited_values(&hierarchy, &nodes);
5486
5487 assert_eq!(c.computed_values.len(), 2);
5488 assert_eq!(changed.len(), 2, "both nodes gained a computed value");
5489
5490 let (t, v) = &c.computed_values[1][0];
5491 assert_eq!(*t, CssPropertyType::FontSize);
5492 assert_eq!(v.origin, CssPropertyOrigin::Inherited);
5493 assert!(close(font_size_parts(&v.property).unwrap().1, 20.0));
5494
5495 assert_eq!(c.computed_values[0][0].1.origin, CssPropertyOrigin::Own);
5497 }
5498
5499 #[test]
5500 fn compute_inherited_values_resolves_a_child_em_against_the_parent_px() {
5501 let hierarchy = two_node_hierarchy();
5502 let nodes = vec![
5503 div_with(vec![font_size(PixelValue::px(20.0))]),
5504 div_with(vec![font_size(PixelValue::em(2.0))]),
5505 ];
5506 let mut c = CssPropertyCache::empty(2);
5507 c.compute_inherited_values(&hierarchy, &nodes);
5508
5509 let (t, v) = &c.computed_values[1][0];
5510 assert_eq!(*t, CssPropertyType::FontSize);
5511 assert_eq!(v.origin, CssPropertyOrigin::Own);
5512 let (metric, n) = font_size_parts(&v.property).unwrap();
5513 assert_eq!(metric, SizeMetric::Px, "resolved to absolute px");
5514 assert!(close(n, 40.0), "2em of the parent's 20px, got {n}");
5515 }
5516
5517 #[test]
5518 fn compute_inherited_values_is_idempotent_on_a_second_run() {
5519 let hierarchy = two_node_hierarchy();
5520 let nodes = vec![
5521 div_with(vec![font_size(PixelValue::px(20.0))]),
5522 NodeData::create_div(),
5523 ];
5524 let mut c = CssPropertyCache::empty(2);
5525 assert_eq!(c.compute_inherited_values(&hierarchy, &nodes).len(), 2);
5526 assert!(
5527 c.compute_inherited_values(&hierarchy, &nodes).is_empty(),
5528 "nothing changed the second time around"
5529 );
5530 }
5531
5532 #[test]
5533 fn compute_inherited_values_on_an_empty_tree_does_not_panic() {
5534 let mut c = CssPropertyCache::empty(0);
5535 assert!(c.compute_inherited_values(&[], &[]).is_empty());
5536 assert!(c.computed_values.is_empty());
5537 }
5538
5539 fn one_node_scaffold() -> (NodeHierarchyItemVec, NodeDataContainer<CascadeInfo>) {
5544 (
5545 vec![NodeHierarchyItem::zeroed()].into(),
5546 NodeDataContainer::new(vec![CascadeInfo {
5547 index_in_parent: 0,
5548 is_last_child: true,
5549 }]),
5550 )
5551 }
5552
5553 #[test]
5554 fn restyle_with_an_empty_stylesheet_flattens_and_yields_no_tags() {
5555 let (hierarchy, cascade) = one_node_scaffold();
5556 let nodes = NodeDataContainer::new(vec![NodeData::create_div()]);
5557 let non_leaf: ParentWithNodeDepthVec = Vec::new().into();
5558 let mut css = Css::empty();
5559
5560 let mut c = CssPropertyCache::empty(1);
5561 let tags = c.restyle(
5562 &mut css,
5563 &nodes.as_ref(),
5564 &hierarchy,
5565 &non_leaf,
5566 &cascade.as_ref(),
5567 );
5568
5569 assert!(tags.is_empty(), "a plain div needs no hit-test tag");
5570 assert!(
5571 c.css_props.is_flattened(),
5572 "restyle must leave css_props in read phase"
5573 );
5574 assert!(c.resolved_font_sizes_px.get().is_none());
5575 }
5576
5577 #[test]
5578 fn generate_tag_ids_skips_inert_nodes_and_tags_interactive_ones() {
5579 let (hierarchy, _) = one_node_scaffold();
5580
5581 let inert = NodeDataContainer::new(vec![NodeData::create_div()]);
5582 let c = CssPropertyCache::empty(1);
5583 assert!(c.generate_tag_ids(&inert.as_ref(), &hierarchy).is_empty());
5584
5585 let hoverable = NodeDataContainer::new(vec![div_with_pseudo(
5587 vec![width_px(1.0)],
5588 PseudoStateType::Hover,
5589 )]);
5590 let tags = c.generate_tag_ids(&hoverable.as_ref(), &hierarchy);
5591 assert_eq!(tags.len(), 1);
5592 assert_eq!(
5593 tags[0].node_id.into_crate_internal(),
5594 Some(NodeId::new(0))
5595 );
5596 }
5597
5598 #[test]
5599 fn generate_tag_ids_tags_a_node_with_a_cursor_declaration() {
5600 let (hierarchy, _) = one_node_scaffold();
5601 let nodes = NodeDataContainer::new(vec![div_with(vec![CssProperty::const_none(
5602 CssPropertyType::Cursor,
5603 )])]);
5604 let c = CssPropertyCache::empty(1);
5605 assert_eq!(c.generate_tag_ids(&nodes.as_ref(), &hierarchy).len(), 1);
5606 }
5607
5608 #[test]
5609 fn generate_tag_ids_on_an_empty_dom_yields_nothing() {
5610 let nodes: NodeDataContainer<NodeData> = NodeDataContainer::new(Vec::new());
5611 let hierarchy: NodeHierarchyItemVec = Vec::new().into();
5612 let c = CssPropertyCache::empty(0);
5613 assert!(c.generate_tag_ids(&nodes.as_ref(), &hierarchy).is_empty());
5614 }
5615
5616 #[cfg(feature = "std")]
5621 #[test]
5622 fn css_prop_type_label_is_interned_and_distinct_per_variant() {
5623 let a = CssPropertyCache::css_prop_type_label(&CssPropertyType::Width);
5624 let b = CssPropertyCache::css_prop_type_label(&CssPropertyType::Width);
5625 assert!(!a.is_empty());
5626 assert_eq!(
5627 a.as_ptr(),
5628 b.as_ptr(),
5629 "the label table must leak at most one &'static str per variant"
5630 );
5631
5632 let other = CssPropertyCache::css_prop_type_label(&CssPropertyType::Height);
5633 assert_ne!(a, other);
5634 }
5635
5636 #[cfg(feature = "std")]
5637 #[test]
5638 fn drain_css_prop_counts_is_sorted_descending_and_drains() {
5639 let first = drain_css_prop_counts();
5642 for w in first.windows(2) {
5643 assert!(w[0].1 >= w[1].1, "counts must be sorted descending");
5644 }
5645 assert!(
5646 drain_css_prop_counts().is_empty(),
5647 "a drained counter comes back empty"
5648 );
5649 }
5650}