Skip to main content

kozan_core/
lifecycle.rs

1//! Document lifecycle — state machine enforcing phase ordering.
2//!
3//! Chrome equivalent: `DocumentLifecycle` in `core/dom/document_lifecycle.h`.
4//!
5//! Phases progress in strict order:
6//! ```text
7//! VisualUpdatePending → InStyleRecalc → StyleClean
8//!                     → InLayout → LayoutClean
9//!                     → InPrePaint → PrePaintClean
10//!                     → InPaint → PaintClean
11//! ```
12//!
13//! Each phase gates the next. You cannot run layout before style is clean,
14//! and you cannot paint before layout is clean.
15
16/// The current lifecycle state of a document.
17///
18/// Chrome: `DocumentLifecycle::LifecycleState`.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
20pub enum LifecycleState {
21    /// Something visual changed — needs work.
22    VisualUpdatePending,
23    /// Style recalc is running.
24    InStyleRecalc,
25    /// Styles computed — ready for layout.
26    StyleClean,
27    /// Layout is running.
28    InLayout,
29    /// Layout complete — ready for paint.
30    LayoutClean,
31    /// Pre-paint (paint property trees) is running.
32    InPrePaint,
33    /// Pre-paint complete.
34    PrePaintClean,
35    /// Paint (display list generation) is running.
36    InPaint,
37    /// All phases complete — everything clean.
38    #[default]
39    PaintClean,
40}
41
42impl LifecycleState {
43    /// Whether this state is "clean" (not in the middle of a phase).
44    #[inline]
45    #[must_use]
46    pub fn is_clean(self) -> bool {
47        matches!(
48            self,
49            Self::StyleClean | Self::LayoutClean | Self::PrePaintClean | Self::PaintClean
50        )
51    }
52
53    /// Whether layout results are up-to-date.
54    #[inline]
55    #[must_use]
56    pub fn is_layout_clean(self) -> bool {
57        self >= Self::LayoutClean
58    }
59
60    /// Whether paint results are up-to-date.
61    #[inline]
62    #[must_use]
63    pub fn is_paint_clean(self) -> bool {
64        self >= Self::PaintClean
65    }
66
67    /// Mark that a visual update is needed (invalidate all phases).
68    ///
69    /// Chrome: `DocumentLifecycle::SetVisualUpdatePending()`.
70    /// Called when DOM changes, style changes, or viewport resizes.
71    #[inline]
72    pub fn invalidate(&mut self) {
73        *self = Self::VisualUpdatePending;
74    }
75
76    /// Whether any work needs to be done.
77    #[inline]
78    #[must_use]
79    pub fn needs_update(self) -> bool {
80        self != Self::PaintClean
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn default_is_clean() {
90        assert_eq!(LifecycleState::default(), LifecycleState::PaintClean);
91    }
92
93    #[test]
94    fn ordering() {
95        assert!(LifecycleState::VisualUpdatePending < LifecycleState::StyleClean);
96        assert!(LifecycleState::StyleClean < LifecycleState::LayoutClean);
97        assert!(LifecycleState::LayoutClean < LifecycleState::PaintClean);
98    }
99
100    #[test]
101    fn invalidate_resets() {
102        let mut state = LifecycleState::PaintClean;
103        assert!(!state.needs_update());
104        state.invalidate();
105        assert!(state.needs_update());
106        assert_eq!(state, LifecycleState::VisualUpdatePending);
107    }
108
109    #[test]
110    fn is_layout_clean_checks() {
111        assert!(!LifecycleState::StyleClean.is_layout_clean());
112        assert!(LifecycleState::LayoutClean.is_layout_clean());
113        assert!(LifecycleState::PaintClean.is_layout_clean());
114    }
115}