Painting: From Layout Boxes to a Display List
Building a Web Browser Engine: Layout & Rendering
Chapter 7 · Painting: From Layout Boxes to a Display List
Every chapter so far has computed real numbers — widths, positions, line breaks — but none of it has said anything about what actually gets drawn. This chapter builds the bridge: walking a finished layout tree and producing a real, flat, ordered display list of simple paint commands — the exact intermediate representation real browsers build before ever touching a pixel.
A Paint Command, and the Simplest Real Thing to Paint
A background genuinely paints out to the border box, not the content box — real CSS behavior, and exactly why border_box() (Chapter 2) is what gets used here, not content directly.
A Naive Walk Order — and the Real Visual Bug It Causes
Looks harmless — walk the tree, collect commands. But a display list is painted in order: each command draws on top of everything already painted before it, exactly the way a real rasterizer works.
layout_block pipeline, not hand-faked rectangles): box A (background: red, filling a 300×50 area) containing box B (background: blue, 100×50, sitting fully inside A). The naive walk produces the display list ['blue', 'red'] — B's command comes first, A's comes after. Simulating what's actually visible at a point sitting inside both boxes, (50, 25): the answer comes out red. A's own background, painted last, completely covers B's — a real browser would show B's blue box sitting visibly on top of A's red one, not swallowed by it.
['red', 'blue']. The same overlap point now correctly resolves to blue — B visibly sits on top of A, exactly matching real CSS stacking (a child paints in front of its own parent). A point inside A but genuinely outside B's own bounds, (250, 25), resolves to red in both the naive and fixed versions — confirming the bug specifically affects the overlap region, not areas where only the parent's background was ever going to be visible anyway.
Where This Connects
| This chapter's finding | What it connects to |
|---|---|
| A flat, ordered display list, painted strictly in sequence | Chapter 8's own software rasterizer — this exact display list is the direct input; the rasterizer's own job is simply to execute each command in order onto a real pixel buffer |
Background painting out to the border edge, using border_box() | Chapter 2's own box-model machinery, reused unchanged — this chapter needed no new geometry code at all, only a new way of walking the tree that was already fully built |
| Paint order matching nesting depth, verified at three levels deep | Chapter 9's own stacking and paint-order chapter, which will need to handle real CSS cases (like z-index) where paint order and DOM nesting order genuinely diverge — this chapter's own "parent first, then children" rule is the honest, simpler default that real CSS uses whenever nothing overrides it |
Hands-On Exercises
Build a container with a single child whose own background-color is left at the default transparent. Run it through build_display_list() and confirm the resulting list is completely empty — not a command with a transparent color, genuinely no command at all — then explain which specific line in paint_background() is responsible.
Build a genuinely three-level-deep nested layout — an outer box, a middle box inside it, and an inner box inside the middle one — each with its own distinct background color. Run it through build_display_list() and confirm the resulting paint order matches the nesting depth exactly: outermost first, innermost last.
Reproduce this chapter's own two-box bug directly: lay out box A (background:red) containing box B (background:blue) using the real layout pipeline, then build display lists with both build_display_list_naive() and build_display_list(). Compute color_at_point() for a point inside both boxes and confirm the two versions disagree. Identify the exact two-line reordering responsible for the difference.
Chapter 7 Quick Reference
- Display list: a flat, ordered sequence of simple paint commands, built by walking the finished layout tree
- Background painting: paints to the border box, not the content box — reuses Chapter 2's own
border_box()unchanged - Real bug found and fixed: recursing into children before appending a box's own background command puts the parent's background LATER (painted on top) — verified completely hiding a real child's background wherever the two overlap
- The fix: paint a box's own background first, then recurse — matching real CSS paint order (parent, then children, outermost to innermost)
- Verified: the fix holds at three levels of nesting, not just two, and a transparent background genuinely produces no command at all
- Next chapter: A Software Rasterizer — turning this display list into an actual pixel buffer, verified pixel by pixel