This document is a work in progress. More details about each stage will be added over time.
Pipeline overview
The LibWeb rendering pipeline consists of several sequential stages: Let’s explore each stage in detail.Resource loading
The pipeline begins when a URL needs to be loaded.LibWeb makes an IPC call to RequestServer, asking it to start loading the requested URL.
- DNS resolution via LookupServer
- HTTP/HTTPS protocol handling
- Response header parsing
- Streaming response data back to WebContent
HTML parsing
The HTML parser transforms an input stream into a DOM tree.Parser responsibilities
- Determine character encoding
- Tokenize the HTML input
- Build the DOM tree structure
- Handle malformed HTML gracefully
Implementation details
LibWeb implements the HTML tokenization and parsing algorithms as specified in the HTML standard:- The tokenizer and parser are separate but deeply interconnected
- They reach into each other and update state in certain situations
- The parser must handle the
document.write()API, which allows JavaScript to inject input
Programmatic injection
CSS parsing
As stylesheets are encountered, the CSS parser processes them.CSS parser responsibilities
- Tokenize CSS syntax
- Build the CSSOM (CSS Object Model)
- Handle syntax errors gracefully
- Preserve unresolved values with variables
CSS values containing variables (custom properties) cannot be resolved until the cascade runs. These are kept as “unresolved” values.
CSS object model
The parser builds a structured representation:- Stylesheets: Collection of rules
- Rules: Selectors and declarations
- Declarations: Property-value pairs
- Values: Typed CSS values (lengths, colors, etc.)
JavaScript parsing and execution
When<script> tags are encountered, LibJS takes over.
JavaScript pipeline
- JS parser: Tokenizes and parses JavaScript source
- AST builder: Constructs an Abstract Syntax Tree
- Interpreter: Executes the AST
- Garbage collector: Reclaims unused memory (mark & sweep)
Execution model
Style computation
Style computation determines which CSS values apply to each DOM element.Selector matching
A CSS selector is represented by theCSS::Selector class.
Given this selector:
Optimization: selector bucketing
LibWeb minimizes the number of selectors evaluated for each element using a bucketing cache in StyleComputer. Rules are divided into buckets based on their rightmost complex selector:- A selector matching class
foois only evaluated against elements with classfoo - A selector matching tag
divis only evaluated against<div>elements - Universal selectors (
*) must be evaluated against all elements
The CSS cascade
The C in CSS is for “cascading”The cascade determines the final property values by evaluating all applicable CSS declarations in order. Cascade order factors:
- Origin: User-agent → author → user
- Importance: Normal →
!important - Specificity: ID > class > tag
- Order: Later rules override earlier ones
Cascade origins
LibWeb separates CSS rules by cascade origin:- User-agent: Built-in styles (processed first)
- Author: Page styles (processed second)
- User: Custom user stylesheets (not yet supported)
The user-agent stylesheet lives in the LibWeb source code at
Libraries/LibWeb/CSS/Default.css.Computed values
The end product is a fully populatedStyleProperties object:
- Contains a
StyleValuefor eachCSS::PropertyID - These are computed values in spec terms
- Not the same as
getComputedStyle()(which returns resolved values)
Resolving CSS custom properties
Custom properties (variables) are resolved during the cascade:Building the layout tree
The layout tree combines the DOM with computed styles to prepare for layout.The layout tree is essentially the “box tree” from CSS specifications, with adaptations for better use of C++ type system.
DOM to layout mapping
There isn’t a 1:1 mapping between DOM nodes and layout nodes:- Elements with
display: nonegenerate no layout node - Some elements generate multiple layout nodes
- Anonymous boxes are created to satisfy layout constraints
Tree fix-ups
Several tree transformations occur:Block container invariant
Block containers must have either all block-level children or all inline-level children:Table fix-ups
If individual table component boxes are found outside proper table context, anonymous table boxes are generated:List item markers
Fordisplay: list-item boxes, a box representing the bullet or number marker is inserted if needed.
Layout
Layout calculates the position and size of every box.Initial containing block
Layout starts at the ICB (Initial Containing Block):- Corresponds to the DOM document node
- Sized to the viewport dimensions
- Creates the root Block Formatting Context
Formatting contexts
CSS defines several formatting contexts:Block Formatting Context
Lays out block-level boxes vertically with margin collapse
Inline Formatting Context
Generates line boxes with inline-level content
Flex Formatting Context
Implements CSS Flexbox layout algorithm
Table Formatting Context
Handles table layout with rows and columns
LibWeb also defines
SVGFormattingContext for embedded SVG content. This simplifies implementation but isn’t part of the SVG specification.Block-level layout
Block Formatting Context (BFC) lays out its children sequentially:- Children are positioned along the block axis (vertically)
- Block-axis margins between adjacent boxes collapse
- Each child computes its own size based on its content
Floating boxes
Floating boxes (float: left or float: right) have special behavior:
- Compute where the box would be positioned if not floating
- Push the box toward the requested edge (left or right)
- If it collides with another float, stack next to that float
- BFC tracks floating boxes on both sides
Inline-level layout
When a BFC encounters inline-level children, it creates an Inline Formatting Context (IFC).Line box generation
IFC’s job is to generate line boxes:- A sequence of inline content fragments
- Laid out along the inline axis (horizontally in LTR)
- Stored in the IFC’s containing block box
Main classes involved
- IFC creates a
LineBuilderandInlineLevelIterator - Traverses inline content by calling
InlineLevelIterator::next() - Passes items to
LineBuilder, which adds them to the current line - When a line fills up, inserts a break and starts a new line
Available space tracking
IFC tracks how much space is available on the current line:- Start with the width of the IFC’s containing block
- Subtract space occupied by floating boxes on both sides
- Query the parent BFC for floats intersecting the line’s Y coordinate
LayoutState object
The result of layout is aLayoutState object:
- Contains CSS “used values” (final metrics)
- Includes line boxes for inline content
- Can be committed via
commit()or discarded - Enables non-destructive “immutable” layouts for measurements
Paintable and the paint tree
After layout completes, LibWeb generates the paint tree.Paint tree structure
The paint tree hangs off the layout tree:- Access via
Layout::Node::paintable() - Convenience accessor:
DOM::Node::paintable() - Not every layout node has a paintable
Paintable responsibilities
Paintable objects have fully finalized metrics and two main jobs:
- Painting: Render visual content to bitmaps
- Hit testing: Determine what element is at given coordinates
Unlike
Layout::Node (box tree with style), Paintable is an object with finalized metrics optimized for rendering.Relationship to layout nodes
Every paintable has a corresponding layout node:- Painting code reaches into layout nodes for some information
- Avoids duplicating data between layout and paint trees
- Not every layout node has a paintable (invisible elements may not)
Stacking contexts
Before painting, LibWeb creates the stacking context tree.What are stacking contexts?
Stacking contexts provide a 3-dimensional layering model:- Places content along a Z-axis
- Controls paint order for overlapping elements
- The
z-indexproperty operates within stacking contexts
Stacking context creation
Common stacking context triggers:- Root element (ICB)
- Positioned elements with
z-indexother thanauto - Elements with
opacityless than 1 - Elements with
transform,filter, or other visual effects - Flex/grid items with
z-indexother thanauto
Stacking context tree
The stacking context tree structure:- Rooted at the ICB
- Can have zero or more descendant stacking contexts
- Each descendant attached to a corresponding layout node
Painting
Painting is the final stage that generates pixels.Paint order
LibWeb follows the paint order specified in CSS2 Appendix E:Stacking context paint order
Painting is driven through stacking contexts:- Stacking contexts are painted back-to-front (tree order)
- Within each stacking context, paint in phase order (above)
- Nested stacking contexts painted recursively
Paint phases
For each stacking context, painting occurs in distinct phases:Complete pipeline example
Let’s trace a simple page through the entire pipeline:- Resource loading: RequestServer fetches the HTML
- HTML parsing: Builds DOM with
html,head,style,body,div, and text nodes - CSS parsing: Parses style rules into CSSOM
- Style computation: Computes styles for each element, cascading user-agent + author styles
- Layout tree building: Creates layout nodes for visible elements
- Layout: ICB creates BFC, div positioned and sized, text measured
- Paint tree building: Creates paintables for ICB, body, div, and text
- Stacking contexts: Root stacking context at ICB
- Painting: Paints yellow background, then text
Performance considerations
Incremental rendering
LibWeb can start rendering before the page fully loads:- HTML parser operates on streaming input
- Style and layout computed incrementally
- Painting occurs as content becomes available
Reflows and repaints
Changes to the page can trigger partial re-execution:- Repaint: Only painting phase (color change)
- Reflow: Layout + painting (size change)
- Full pipeline: All stages (DOM modification)
Related documentation
Architecture overview
High-level architecture overview
Process architecture
Multi-process design and WebContent process