Something went wrong. Try again.
we (web engine): Experimental web browser project to understand the limits of Claude
Something went wrong. Try again.
Rust
at url-parser
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163use std::cell::RefCell;
use we_html::parse_html;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#"<!DOCTYPE html><html><head><title>we browser</title><style>h1 { color: blue; }h2 { color: green; }p { color: #333333; }</style></head><body><h1>Hello from we!</h1><p>This is a from-scratch web browser engine written in pure Rust.</p><p>Zero external crate dependencies. Every subsystem is implemented in Rust.</p><h2>Features</h2><p>HTML5 tokenizer, DOM tree, block layout, CSS cascade, and software rendering.</p></body></html>"#;
/// Browser state kept in thread-local storage so the resize handler can/// access it. All AppKit callbacks run on the main thread.struct BrowserState { html: String, font: Font, bitmap: Box<BitmapContext>, view: appkit::BitmapView,}
thread_local! { static STATE: RefCell<Option<BrowserState>> = const { RefCell::new(None) };}
/// 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; if width == 0 || height == 0 { return; }
// Parse HTML into DOM. let doc = parse_html(html);
// Extract CSS from <style> elements and resolve computed styles. let stylesheets = extract_stylesheets(&doc); let styled = match resolve_styles(&doc, &stylesheets) { Some(s) => s, None => return, };
// Layout using styled tree (CSS-driven). let tree = layout(&styled, &doc, width as f32, height as f32, font);
let mut renderer = Renderer::new(width, height); renderer.paint(&tree, font);
// Copy rendered pixels into the bitmap context's buffer. let src = renderer.pixels(); let dst = bitmap.pixels_mut(); let len = src.len().min(dst.len()); dst[..len].copy_from_slice(&src[..len]);}
/// Called by the platform crate when the window is resized.fn handle_resize(width: f64, height: f64) { STATE.with(|state| { let mut state = state.borrow_mut(); let state = match state.as_mut() { Some(s) => s, None => return, };
let w = width as usize; let h = height as usize; if w == 0 || h == 0 { return; }
// Create a new bitmap context with the new dimensions. let mut new_bitmap = match BitmapContext::new(w, h) { Some(b) => Box::new(b), None => return, };
render_page(&state.html, &state.font, &mut new_bitmap);
// Swap in the new bitmap and update the view's pointer. state.bitmap = new_bitmap; state.view.update_bitmap(&state.bitmap); });}
fn main() { // Load HTML from file argument or use default page. let html = match std::env::args().nth(1) { Some(path) => match std::fs::read_to_string(&path) { Ok(content) => content, Err(e) => { eprintln!("Error reading {}: {}", path, e); std::process::exit(1); } }, None => DEFAULT_HTML.to_string(), };
// Load a system font for text rendering. let font = match font::load_system_font() { Ok(f) => f, Err(e) => { eprintln!("Error loading system font: {:?}", e); std::process::exit(1); } };
let _pool = appkit::AutoreleasePool::new();
let app = appkit::App::shared(); app.set_activation_policy(appkit::NS_APPLICATION_ACTIVATION_POLICY_REGULAR); appkit::install_app_delegate(&app);
let window = appkit::create_standard_window("we"); appkit::install_window_delegate(&window); window.set_accepts_mouse_moved_events(true);
// Initial render at the default window size (800x600). let mut bitmap = Box::new(BitmapContext::new(800, 600).expect("failed to create bitmap context")); render_page(&html, &font, &mut bitmap);
// Create the view backed by the rendered bitmap. let frame = appkit::NSRect::new(0.0, 0.0, 800.0, 600.0); let view = appkit::BitmapView::new(frame, &bitmap); window.set_content_view(&view.id());
// Store state for the resize handler. STATE.with(|state| { *state.borrow_mut() = Some(BrowserState { html, font, bitmap, view, }); });
// Register resize handler so re-layout happens on window resize. appkit::set_resize_handler(handle_resize);
window.make_key_and_order_front(); app.activate(); app.run();}