From 671d128b47522fc43f0a69aa0c7d33c0e1137bf3 Mon Sep 17 00:00:00 2001 From: Pierre Le Fevre Date: Thu, 5 Mar 2026 20:31:33 +0100 Subject: [PATCH] Handle +

Hello from we!

This is a from-scratch web browser engine written in pure Rust.

Zero external crate dependencies. Every subsystem is implemented in Rust.

Features

-

HTML5 tokenizer, DOM tree, block layout, and software rendering.

+

HTML5 tokenizer, DOM tree, block layout, CSS cascade, and software rendering.

"#; @@ -33,7 +41,7 @@ thread_local! { static STATE: RefCell> = const { RefCell::new(None) }; } -/// Re-run the full pipeline: parse → layout → render → copy to bitmap. +/// Re-run the full pipeline: parse → extract CSS → resolve styles → layout → render → copy to bitmap. fn render_page(html: &str, font: &Font, bitmap: &mut BitmapContext) { let width = bitmap.width() as u32; let height = bitmap.height() as u32; @@ -41,7 +49,14 @@ fn render_page(html: &str, font: &Font, bitmap: &mut BitmapContext) { return; } + // Parse HTML into DOM. let doc = parse_html(html); + + // Extract CSS from + + +

Title

+

Paragraph

+ +"#; + + let doc = we_html::parse_html(html); + let sheets = extract_stylesheets(&doc); + assert_eq!(sheets.len(), 1); + + let styled = resolve_styles(&doc, &sheets).unwrap(); + // styled root is + // Find body (first visible child) + let body = &styled.children[0]; + + // Find h1 and p + let h1 = &body.children[0]; + let p = &body.children[1]; + + assert_eq!(h1.style.color, Color::rgb(0, 0, 255)); // blue + assert_eq!(p.style.color, Color::rgb(255, 0, 0)); // red + assert_eq!(p.style.font_size, 24.0); +} + +#[test] +fn html_with_multiple_style_elements() { + let html = r#" + + + + + +

text

+"#; + + let doc = we_html::parse_html(html); + let sheets = extract_stylesheets(&doc); + assert_eq!(sheets.len(), 2); + + let styled = resolve_styles(&doc, &sheets).unwrap(); + let body = &styled.children[0]; + let p = &body.children[0]; + + // Later stylesheet wins. + assert_eq!(p.style.color, Color::rgb(0, 128, 0)); +} + +#[test] +fn html_with_inline_style_attribute() { + let html = r#" + + + + + +

text

+ +"#; + + let doc = we_html::parse_html(html); + let sheets = extract_stylesheets(&doc); + + let styled = resolve_styles(&doc, &sheets).unwrap(); + let body = &styled.children[0]; + let p = &body.children[0]; + + // Inline style overrides stylesheet. + assert_eq!(p.style.color, Color::rgb(0, 0, 255)); +} + +#[test] +fn html_no_style_uses_ua_defaults() { + let html = r#" + + +

Title

+

Text

+ +"#; + + let doc = we_html::parse_html(html); + let sheets = extract_stylesheets(&doc); + assert!(sheets.is_empty()); + + let styled = resolve_styles(&doc, &sheets).unwrap(); + let body = &styled.children[0]; + + // Body should have 8px margin from UA stylesheet. + assert_eq!(body.style.margin_top, LengthOrAuto::Length(8.0)); + + // h1 should be block with bold and 2em font-size. + let h1 = &body.children[0]; + assert_eq!(h1.style.display, Display::Block); + assert_eq!(h1.style.font_weight, FontWeight(700.0)); + assert_eq!(h1.style.font_size, 32.0); // 2em * 16px +} + +#[test] +fn style_element_display_none_in_styled_tree() { + let html = r#" + + + + +

text

+"#; + + let doc = we_html::parse_html(html); + let sheets = extract_stylesheets(&doc); + let styled = resolve_styles(&doc, &sheets).unwrap(); + + // The and + + +

Important

+

Normal

+ +"#; + + let doc = we_html::parse_html(html); + let sheets = extract_stylesheets(&doc); + let styled = resolve_styles(&doc, &sheets).unwrap(); + let body = &styled.children[0]; + + let highlight_p = &body.children[0]; + let normal_p = &body.children[1]; + + assert_eq!(highlight_p.style.color, Color::rgb(255, 0, 0)); + assert_eq!(highlight_p.style.background_color, Color::rgb(255, 255, 0)); + + // Normal p inherits default color (black). + assert_eq!(normal_p.style.color, Color::rgb(0, 0, 0)); +} -- 2.51.2