灵符lingfu
H

horizon-browser

↓ 0 downloads

Horizon: a real box-model HTML/CSS layout engine with a Canvas 2D surface, no_std + alloc, zero dependencies -- LingOS's next-generation browser engine, sibling to bring-browser

Keywords browser, html, css, layout, canvas, no_std, lingos
lingfu install horizon-browser
LicenseMIT
AuthorsLing Lin <taellinglin@gmail.com>
READMECode / FilesVersionsDependency GraphDependentsSecurityBuildsSettings

README

horizon — a real browser engine for LingOS

horizon is LingOS's second web engine: a no_std + alloc, zero-dependency HTML/CSS box-model layout library, sibling to [bring-browser](../bring-browser). Where bring is a links/lynx-style text-flow engine (fixed line buffers, no allocation, no real box model), horizon builds an actual DOM tree, cascades CSS onto it, lays it out with real block/inline boxes, and paints a pixel display list — including a <canvas> element with a real 2D drawing surface. It is not a JS engine and does not pretend to be WebKit/Gecko/Servo; see the roadmap below for what's honestly in scope today.

The engine never touches a screen itself. Like bring, it hands a laid-out Page (a display list of rects/text/canvas/image items, in pixels) to a thin embedder — LingOS's desktop compositor (ling-kernel/src/drivers/horizon.rs) walks that list with the kernel's existing framebuffer + font8x8 primitives, the same way ling-kernel/src/drivers/browser.rs already does for bring.

What renders today (v0.1)

  • DOM: html, head, body, div, span, p, h1–h6, a[href], b/strong, i/em, br, ul/ol/li, img[src], canvas[width][height][id], pre, title; script/style contents parsed for CSS only, never executed or rendered as text.
  • CSS: inline style="" and <style> blocks, tag + .class + #id selectors, cascade by specificity + source order. Properties: display (block/inline/none), color, background-color, width, height, margin, padding (single value, uniform on all sides — shorthand per-side values are roadmap), font-weight: bold, text-align.
  • Layout: real normal-flow box model. Block boxes stack vertically with margin/padding/explicit-or-auto width; inline content (text + inline elements) flows and wraps within the content box at the embedder's glyph metrics (monospace only — horizon takes a char_w/line_h in pixels, same spirit as bring's cols parameter, so it stays font-agnostic). No floats, no flexbox, no grid, no absolute/relative positioning yet.
  • Canvas: <canvas> allocates a real CanvasSurface (Vec<u32> pixel buffer) with a 2D drawing API — fill_rect, stroke_rect, fill_circle, line, set_pixel, clear — addressable by the embedder or host Rust code via Page::canvas_mut(id). Scripted canvas (a page's own <script> calling getContext("2d")) is not implemented; there is no JS engine yet, so nothing drives the canvas except native code that already has a &mut Page. This is the same honest limitation bring states for scripting generally.
  • Output: a flat Vec<DisplayItem> (Rect, Text, Image, Canvas) in paint order, plus link/image tables — the embedder walks it once per frame.

Roadmap, honestly staged

  1. CSS box model completeness (near): per-side margin/padding/border shorthand, border painting, %-relative widths, position: relative.
  2. Proportional text + inline-block (mid): today's layout assumes a monospace glyph grid because that's what LingOS's font8x8 gives it; real font metrics (variable glyph widths, LingOS's font_unicode table) need layout to measure text instead of counting characters.
  3. Flexbox subset (mid-far): the single most-requested real-world layout primitive; a constrained one-axis flex (display: flex, flex-direction, justify-content, no wrapping) before anything fancier.
  4. Scripting (far): if it lands, a small interpreted subset sharing LingOS's in-kernel VM work, the same stance bring takes — not a JS engine bolted onto a kernel. Until then, <canvas> is a real paintable surface that only native code can drive, and pages with script-driven behavior render their static markup only.

Each stage lands with host tests in this repo (cargo test, no kernel required) before it's wired into ling-kernel.

← Back to index