1#![allow(dead_code)]
8
9use alloc::vec::Vec;
10
11use super::{
12 css_parser::CssParser,
13 dom::Document,
14 layout::{build_layout_tree, LayoutBox},
15 paint::{build_display_list, DisplayList, Painter, PixelRect},
16 style::StyleResolver,
17 tree_builder::TreeBuilder,
18};
19
20pub fn render_html(html: &str, width: i32, height: i32) -> Vec<u32> {
25 render_html_with_css(html, "", width, height)
26}
27
28pub fn render_html_with_css(html: &str, css: &str, width: i32, height: i32) -> Vec<u32> {
30 let doc = TreeBuilder::build(html);
31 let stylesheet = CssParser::parse(css);
32
33 let mut resolver = StyleResolver::new();
34 resolver.add_stylesheet(stylesheet);
35
36 let layout = build_layout_tree(&doc, &resolver, width, height);
37 let display_list = build_display_list(&layout, 0);
38
39 let mut painter = Painter::new(width as usize, height as usize);
40 painter.paint(&display_list);
41
42 painter.pixels
43}
44
45pub fn render_to_layout(html: &str, css: &str, width: i32, height: i32) -> LayoutBox {
47 let doc = TreeBuilder::build(html);
48 let stylesheet = CssParser::parse(css);
49
50 let mut resolver = StyleResolver::new();
51 resolver.add_stylesheet(stylesheet);
52
53 build_layout_tree(&doc, &resolver, width, height)
54}
55
56pub fn render_to_display_list(
58 html: &str,
59 css: &str,
60 width: i32,
61 height: i32,
62) -> (LayoutBox, DisplayList) {
63 let doc = TreeBuilder::build(html);
64 let stylesheet = CssParser::parse(css);
65
66 let mut resolver = StyleResolver::new();
67 resolver.add_stylesheet(stylesheet);
68
69 let layout = build_layout_tree(&doc, &resolver, width, height);
70 let display_list = build_display_list(&layout, 0);
71
72 (layout, display_list)
73}
74
75pub fn parse_html(html: &str) -> Document {
77 TreeBuilder::build(html)
78}
79
80pub fn region_contains_color(pixels: &[u32], width: usize, rect: &PixelRect, color: u32) -> bool {
82 for y in rect.y..(rect.y + rect.height) {
83 for x in rect.x..(rect.x + rect.width) {
84 if x >= 0 && y >= 0 {
85 let idx = y as usize * width + x as usize;
86 if idx < pixels.len() && pixels[idx] == color {
87 return true;
88 }
89 }
90 }
91 }
92 false
93}
94
95pub fn region_is_solid_color(pixels: &[u32], width: usize, rect: &PixelRect, color: u32) -> bool {
97 for y in rect.y..(rect.y + rect.height) {
98 for x in rect.x..(rect.x + rect.width) {
99 if x >= 0 && y >= 0 {
100 let idx = y as usize * width + x as usize;
101 if idx < pixels.len() && pixels[idx] != color {
102 return false;
103 }
104 }
105 }
106 }
107 true
108}
109
110pub fn count_unique_colors(pixels: &[u32], width: usize, rect: &PixelRect) -> usize {
112 let mut colors: Vec<u32> = Vec::new();
113 for y in rect.y..(rect.y + rect.height) {
114 for x in rect.x..(rect.x + rect.width) {
115 if x >= 0 && y >= 0 {
116 let idx = y as usize * width + x as usize;
117 if idx < pixels.len() && !colors.contains(&pixels[idx]) {
118 colors.push(pixels[idx]);
119 }
120 }
121 }
122 }
123 colors.len()
124}
125
126pub fn find_layout_box_by_tag<'a>(
128 layout: &'a LayoutBox,
129 doc: &Document,
130 tag: &str,
131) -> Option<&'a LayoutBox> {
132 if let Some(node_id) = layout.node_id {
133 if doc.tag_name(node_id) == Some(tag) {
134 return Some(layout);
135 }
136 }
137 for child in &layout.children {
138 if let Some(found) = find_layout_box_by_tag(child, doc, tag) {
139 return Some(found);
140 }
141 }
142 None
143}
144
145pub fn count_layout_boxes(layout: &LayoutBox) -> usize {
147 let mut count = 1;
148 for child in &layout.children {
149 count += count_layout_boxes(child);
150 }
151 count
152}
153
154#[cfg(test)]
155mod tests {
156 #[allow(unused_imports)]
157 use alloc::{string::String, vec};
158
159 use super::*;
160
161 #[test]
162 fn test_render_empty() {
163 let pixels = render_html("", 100, 100);
164 assert_eq!(pixels.len(), 10000);
165 }
166
167 #[test]
168 fn test_render_simple_paragraph() {
169 let pixels = render_html("<p>Hello</p>", 200, 200);
170 assert_eq!(pixels.len(), 40000);
171 }
172
173 #[test]
174 fn test_render_with_css() {
175 let pixels = render_html_with_css(
176 "<div>Hello</div>",
177 "div { background-color: #ff0000; width: 100px; height: 50px; }",
178 200,
179 200,
180 );
181 assert_eq!(pixels.len(), 40000);
182 let has_red = pixels.iter().any(|&p| p == 0xFFFF0000);
184 assert!(has_red, "Expected red pixels from background-color");
185 }
186
187 #[test]
188 fn test_render_headings() {
189 let pixels = render_html("<h1>Title</h1><h2>Subtitle</h2><p>Text</p>", 400, 300);
190 assert_eq!(pixels.len(), 120000);
191 }
192
193 #[test]
194 fn test_render_colored_text() {
195 let pixels = render_html_with_css("<p>Red text</p>", "p { color: #ff0000; }", 200, 200);
196 assert_eq!(pixels.len(), 40000);
197 }
198
199 #[test]
200 fn test_render_nested_elements() {
201 let pixels = render_html("<div><p>Nested <b>bold</b></p></div>", 200, 200);
202 assert_eq!(pixels.len(), 40000);
203 }
204
205 #[test]
206 fn test_render_links() {
207 let doc = parse_html("<a href=\"/page\">Click me</a>");
208 let anchors = doc.get_elements_by_tag_name("a");
209 assert_eq!(anchors.len(), 1);
210 assert_eq!(doc.inner_text(anchors[0]), "Click me");
211 }
212
213 #[test]
214 fn test_render_to_layout() {
215 let layout = render_to_layout("<div><p>Hello</p></div>", "", 800, 600);
216 assert!(layout.dimensions.content.width > 0);
217 }
218
219 #[test]
220 fn test_render_to_display_list() {
221 let (layout, display_list) = render_to_display_list(
222 "<div>Hello</div>",
223 "div { background-color: red; height: 50px; }",
224 800,
225 600,
226 );
227 assert!(layout.dimensions.content.width > 0);
228 assert!(!display_list.commands.is_empty());
229 }
230
231 #[test]
232 fn test_parse_html_returns_document() {
233 let doc = parse_html("<html><body><p>Test</p></body></html>");
234 assert!(doc.node_count() > 1);
235 }
236
237 #[test]
238 fn test_region_contains_color() {
239 let mut pixels = alloc::vec![0xFFFFFFFF; 100];
240 pixels[55] = 0xFFFF0000; let rect = PixelRect::new(0, 0, 10, 10);
242 assert!(region_contains_color(&pixels, 10, &rect, 0xFFFF0000));
243 }
244
245 #[test]
246 fn test_region_solid_color() {
247 let pixels = alloc::vec![0xFFFFFFFF; 100];
248 let rect = PixelRect::new(0, 0, 10, 10);
249 assert!(region_is_solid_color(&pixels, 10, &rect, 0xFFFFFFFF));
250 }
251
252 #[test]
253 fn test_region_not_solid() {
254 let mut pixels = alloc::vec![0xFFFFFFFF; 100];
255 pixels[0] = 0xFF000000;
256 let rect = PixelRect::new(0, 0, 10, 10);
257 assert!(!region_is_solid_color(&pixels, 10, &rect, 0xFFFFFFFF));
258 }
259
260 #[test]
261 fn test_count_unique_colors() {
262 let mut pixels = alloc::vec![0xFFFFFFFF; 100];
263 pixels[0] = 0xFF000000;
264 pixels[1] = 0xFFFF0000;
265 let rect = PixelRect::new(0, 0, 10, 10);
266 let count = count_unique_colors(&pixels, 10, &rect);
267 assert_eq!(count, 3);
268 }
269
270 #[test]
271 fn test_count_layout_boxes() {
272 let layout = render_to_layout("<div><p>A</p><p>B</p></div>", "", 800, 600);
273 let count = count_layout_boxes(&layout);
274 assert!(count >= 3, "Expected at least 3 boxes, got {}", count);
275 }
276
277 #[test]
278 fn test_render_background_visible() {
279 let pixels = render_html_with_css(
280 "<div>Content</div>",
281 "div { background-color: #0000ff; width: 200px; height: 100px; }",
282 400,
283 300,
284 );
285 let has_blue = pixels.iter().any(|&p| p == 0xFF0000FF);
286 assert!(has_blue, "Expected blue background pixels");
287 }
288
289 #[test]
290 fn test_render_multiple_backgrounds() {
291 let pixels = render_html_with_css(
292 "<div id='a'>A</div><div id='b'>B</div>",
293 "#a { background-color: #ff0000; height: 50px; } #b { background-color: #0000ff; \
294 height: 50px; }",
295 200,
296 200,
297 );
298 let has_red = pixels.iter().any(|&p| p == 0xFFFF0000);
299 let has_blue = pixels.iter().any(|&p| p == 0xFF0000FF);
300 assert!(has_red, "Expected red background");
301 assert!(has_blue, "Expected blue background");
302 }
303
304 #[test]
305 fn test_render_large_document() {
306 let mut html = String::from("<html><body>");
307 for i in 0..20 {
308 html.push_str(&alloc::format!("<p>Paragraph {}</p>", i));
309 }
310 html.push_str("</body></html>");
311 let pixels = render_html(&html, 800, 600);
312 assert_eq!(pixels.len(), 480000);
313 }
314
315 #[test]
316 fn test_full_pipeline_with_styles() {
317 let html = "<!DOCTYPE html><html><head><style>body { background-color: #eee; } h1 { \
318 color: #333; } p { margin: 10px; \
319 }</style></head><body><h1>Welcome</h1><p>This is a test \
320 page.</p></body></html>";
321 let pixels = render_html(html, 800, 600);
322 assert_eq!(pixels.len(), 480000);
323 }
324}