1use blitz_traits::node_id::NodeId;
2use parley::layout::PositionedLayoutItem;
3
4use crate::BaseDocument;
5
6impl BaseDocument {
7 pub fn print_taffy_tree(&self) {
8 taffy::print_tree(self, taffy::NodeId::from(0usize));
9 }
10
11 pub fn debug_log_node(&self, node_id: NodeId) {
12 let node = &self.nodes[node_id];
13
14 #[cfg(feature = "tracing")]
15 {
16 tracing::info!("Layout: {:?}", node.final_layout());
17 tracing::info!("Style: {:?}", node.style());
18 }
19
20 println!("\nNode {} {}", node.id, node.node_debug_str());
21
22 println!("Attrs:");
23
24 for attr in node.attrs().into_iter().flatten() {
25 println!(" {}: {}", attr.name.local, attr.value);
26 }
27
28 if node.flags.is_inline_root() {
29 let inline_layout = &node
30 .data
31 .downcast_element()
32 .unwrap()
33 .inline_layout_data
34 .as_ref()
35 .unwrap();
36
37 println!(
38 "Size: {}x{}",
39 inline_layout.layout.width(),
40 inline_layout.layout.height()
41 );
42 println!("Text content: {:?}", inline_layout.text);
43 println!("Inline Boxes:");
44 for ibox in inline_layout.layout.inline_boxes() {
45 print!("(id: {}) ", ibox.id);
46 }
47 println!();
48 println!("Lines:");
49 for (i, line) in inline_layout.layout.lines().enumerate() {
50 let metrics = line.metrics();
51 let x = metrics.inline_min_coord;
52 let y = metrics.block_min_coord;
53 let w = metrics.inline_max_coord - metrics.inline_min_coord;
54 let h = metrics.block_max_coord - metrics.block_min_coord;
55 println!("Line {i}: x:{x} y:{y} width:{w} height:{h}");
56 for item in line.items() {
57 print!(" ");
58 match item {
59 PositionedLayoutItem::GlyphRun(run) => {
60 print!(
61 "RUN (x: {}, w: {}) ",
62 run.offset().round(),
63 run.run().advance()
64 )
65 }
66 PositionedLayoutItem::InlineBox(ibox) => print!(
67 "BOX {:?} (id: {} x: {} y: {} w: {}, h: {})",
68 ibox.kind,
69 ibox.id,
70 ibox.x.round(),
71 ibox.y.round(),
72 ibox.width.round(),
73 ibox.height.round()
74 ),
75 }
76 println!();
77 }
78 }
79 }
80
81 let layout = node.final_layout();
82 println!("Layout:");
83 println!(
84 " x: {x} y: {y} w: {width} h: {height} content_w: {content_width} content_h: {content_height}",
85 x = layout.location.x,
86 y = layout.location.y,
87 width = layout.size.width,
88 height = layout.size.height,
89 content_width = layout.content_size.width,
90 content_height = layout.content_size.height,
91 );
92 println!(
93 " border: l:{l} r:{r} t:{t} b:{b}",
94 l = layout.border.left,
95 r = layout.border.right,
96 t = layout.border.top,
97 b = layout.border.bottom,
98 );
99 println!(
100 " padding: l:{l} r:{r} t:{t} b:{b}",
101 l = layout.padding.left,
102 r = layout.padding.right,
103 t = layout.padding.top,
104 b = layout.padding.bottom,
105 );
106 println!(
107 " margin: l:{l} r:{r} t:{t} b:{b}",
108 l = layout.margin.left,
109 r = layout.margin.right,
110 t = layout.margin.top,
111 b = layout.margin.bottom,
112 );
113 println!("Parent: {:?}", node.parent);
114
115 let children: Vec<_> = node
116 .children
117 .iter()
118 .map(|id| &self.nodes[*id])
119 .map(|node| (node.id, node.order(), node.node_debug_str()))
120 .collect();
121 println!("Children: {children:?}");
122
123 println!("Layout Parent: {:?}", node.layout_parent.get());
124
125 let layout_children: Option<Vec<_>> = node.layout_children.borrow().as_ref().map(|lc| {
126 lc.iter()
127 .map(|id| &self.nodes[*id])
128 .map(|node| (node.id, node.order(), node.node_debug_str()))
129 .collect()
130 });
131 if let Some(layout_children) = layout_children {
132 println!("Layout Children: {layout_children:?}");
133 }
134
135 let paint_children: Option<Vec<_>> = node.paint_children.borrow().as_ref().map(|lc| {
136 lc.iter()
137 .map(|id| &self.nodes[*id])
138 .map(|node| (node.id, node.order(), node.node_debug_str()))
139 .collect()
140 });
141 if let Some(paint_children) = paint_children {
142 println!("Paint Children: {paint_children:?}");
143 }
144 }
146}
147
148pub(crate) fn animation_reasons_enabled() -> bool {
160 static ENABLED: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
161 *ENABLED.get_or_init(|| {
162 matches!(
163 std::env::var("BLITZ_ANIMATION_DEBUG").ok().as_deref(),
164 Some("1") | Some("true")
165 )
166 })
167}
168
169#[allow(clippy::too_many_arguments)]
170pub(crate) fn report_animation_reasons(
171 doc_id: usize,
172 canvas: bool,
173 css_animations: bool,
174 subdoc: bool,
175 custom_widget: bool,
176 scroll: bool,
177 scrollbars: bool,
178 nodes: Option<&str>,
179) {
180 use std::sync::Mutex;
181 use std::time::{Duration, Instant};
182
183 static LAST: Mutex<Option<Instant>> = Mutex::new(None);
184 let mut last = LAST.lock().unwrap();
185 let now = Instant::now();
186 if last.is_some_and(|t| now.duration_since(t) < Duration::from_secs(1)) {
187 return;
188 }
189 *last = Some(now);
190 drop(last);
191
192 let mut reasons: Vec<&str> = Vec::new();
193 for (flag, name) in [
194 (canvas, "canvas"),
195 (css_animations, "css-animations"),
196 (subdoc, "subdocument"),
197 (custom_widget, "custom-widget"),
198 (scroll, "scroll-animation"),
199 (scrollbars, "scrollbar-fade"),
200 ] {
201 if flag {
202 reasons.push(name);
203 }
204 }
205
206 eprintln!(
207 "[animating] doc={doc_id} {}{}",
208 reasons.join(","),
209 nodes.map(|n| format!(" {n}")).unwrap_or_default()
210 );
211}