Toolchain Setup
What you need, in the order you need it. Phases 0–2 of the browser track require no local Chromium build — that is deliberate, and it is what keeps the track moving while a 100 GB checkout and a toolchain upgrade happen in the background.
Tier 0 — needed on day one (zero cost)
| Tool | Why |
|---|---|
| Chrome / Chromium (stock) | every observation lab; DevTools; --enable-blink-features |
Chromium Code Search — source.chromium.org/chromium/chromium/src | source navigation with cross-references and blame, no checkout |
Perfetto UI — ui.perfetto.dev | full traces across processes and threads |
| Node.js | the framework-track labs and their spec harnesses |
| The specs — WHATWG HTML/DOM, CSS, Web IDL | the contract the implementation is written against |
That is enough for bi-01, bi-03, and all of fw-*.
DevTools settings worth changing once
- CPU throttling 4×/6× — unthrottled desktop results are not evidence about your users.
- Rendering panel: paint flashing, layer borders, scroll-performance issues, frame rendering stats.
- Performance panel: enable "Screenshots" and "Memory".
- Experiments: enable the timeline's advanced rendering instrumentation if offered.
Tier 1 — the checkout (large, but useful before it compiles)
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git ~/depot_tools
export PATH="$PATH:$HOME/depot_tools" # add to ~/.zshrc
mkdir ~/chromium && cd ~/chromium
caffeinate fetch --git-cache chromium
Budget the disk honestly. Measured on the reference machine (2026-08-10):
| Item | Size |
|---|---|
~/chromium/src | 26 GB |
| git cache mirror | 24 GB |
| one component build, modest symbols | 15–25 GB |
The mirror location comes from cache_dir in ~/chromium/.gclient — it is not ~/.cache, and
confusing the two sends you deleting the wrong thing. fetch --git-cache roughly doubles peak
storage; the mirror is a cache and can be deleted once src is synced.
What the checkout gives you before it ever compiles
This is the part people miss. Without a working compiler you still get:
git grep -n 'CausesFosterParenting' -- third_party/blink/renderer/core/html/parser/
git log -S'mac_sdk_official_version = "26.5"' -- build/config/mac/mac_sdk.gni
git log --follow --oneline -- <file> # survives the ng_* renames
git grep over 30M lines returns in well under a second — faster than Code Search — and
git log -S is the archaeology tool the source-reading ladder depends on. Plus every in-tree
document, every .json5, .idl, and .mojom.
Tier 2 — the build (needed from Phase 3)
cd ~/chromium/src
gn gen out/Default
out/Default/args.gn:
is_debug = false # release codegen: much faster builds
is_component_build = true # small dylibs, fast incremental links — essential
symbol_level = 1 # function names + lines
blink_symbol_level = 2 # full symbols where you set breakpoints
dcheck_always_on = true # keep assertions: the highest-value learning flag
autoninja -C out/Default content_shell # prefer this over `chrome`
gn ls out/Default | grep -E ':(content_shell|blink_tests)$' # never guess target names
dcheck_always_on = true is the flag most people omit and the one that matters most for
learning: DCHECKs are Blink's invariants written as executable assertions, so breaking one
produces a message naming the invariant instead of a confusing misrender.
Known blocker on the reference machine
Chromium trunk requires macOS 26.2+ / Xcode 26.5+ / SDK 26.5 (the requirement landed
2026-05-13). On macOS 15.0 / Xcode 16.2 the build fails on missing SDK symbols such as
posix_spawn_file_actions_addchdir, and no gn flag can fix it — __builtin_available is a runtime
check but the symbol must exist at compile time.
Full diagnosis, the two gn workarounds that get gn gen through, and the ranked options are in
the build guide §2.0.
Tier 3 — debugging and tests
echo "command script import ~/chromium/src/tools/lldb/lldbinit.py" >> ~/.lldbinit
Attach to a renderer, not the browser process:
out/Default/Content\ Shell.app/Contents/MacOS/Content\ Shell \
--renderer-startup-dialog --disable-hang-monitor <url>
lldb -p <pid printed by the dialog>
--disable-hang-monitor matters: without it, sitting at a breakpoint for 30 seconds gets your
renderer killed and you lose the state you were inspecting.
Tests:
autoninja -C out/Default blink_tests
third_party/blink/tools/run_web_tests.py -t Default fast/forms
out/Default/content_shell --run-web-tests <path>
Known failures live in third_party/blink/web_tests/TestExpectations (9,418 lines) — which is also
your candidate pool for a first contribution.
Building this book
bash build.sh # installs a pinned mdBook if absent, writes ./book and ./dist/book
mdbook serve # live reload at http://localhost:3000
python3 tools/gen-summary.py # regenerate SUMMARY.md after adding modules or steps
SUMMARY.md is generated, not hand-maintained: a module has up to 13 pages across ~35 modules,
and a mistyped link silently drops a page from the book.
A standing rule
Every Chromium path, class name, command, and flag in this curriculum has a date attached in PROGRESS.md §7. Re-verify anything older than about six months.
This is not pedantry. In the course of writing this book, two widely-repeated facts turned out to
be retired — the ng_ prefix on Blink layout classes, and TraceWrapperMember<T> for DOM/JS heap
synchronisation — and both would have been asserted confidently from memory. Each took under a
minute to check in the tree.