diff --git a/Cargo.lock b/Cargo.lock index 343c438..6f76422 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,6 +96,7 @@ version = "0.1.0" dependencies = [ "we-css", "we-dom", + "we-html", ] [[package]] diff --git a/crates/browser/src/main.rs b/crates/browser/src/main.rs index 48e7920..018b972 100644 --- a/crates/browser/src/main.rs +++ b/crates/browser/src/main.rs @@ -5,18 +5,26 @@ use we_layout::layout; use we_platform::appkit; use we_platform::cg::BitmapContext; use we_render::Renderer; +use we_style::computed::{extract_stylesheets, resolve_styles}; use we_text::font::{self, Font}; /// Default HTML page shown when no file argument is provided. const DEFAULT_HTML: &str = r#" -we browser + +we browser + +

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)); +}