1. Architecture Overview
Ladybird is an independent web browser built from scratch in C++ (with Rust components for parsing, regex, and Unicode), using custom implementations for every layer of the browser stack: HTML parsing, CSS layout, JavaScript execution, networking, image decoding, and graphics rendering. It originated from the SerenityOS project and is licensed BSD-2-Clause.
The browser employs a multi-process architecture where the UI process orchestrates specialized child processes for web content rendering, network I/O, image decoding, and Web Workers. Inter-process communication uses a custom IPC framework with Unix domain sockets (or Mach ports on macOS).
Architecture Diagram
┌─────────────────────────────────────────────────────────────┐
│ UI Process │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Qt 6 UI │ │ GTK 4 UI │ │AppKit UI │ (one per build) │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ └──────────────┼──────────────┘ │
│ ▼ │
│ LibWebView::Application │
│ (Settings, Bookmarks, CookieJar, HistoryStore) │
│ │ │
│ ┌─────────────────┼─────────────────┐ │
│ ▼ ▼ ▼ │
│ ViewImpl ──IPC── ViewImpl ──IPC── ViewImpl (per tab) │
└────┬─────────────────┬─────────────────┬────────────────────┘
│ IPC │ IPC │ IPC
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│WebContent│ │WebContent│ │WebContent│ (one per tab/site)
│ Process │ │ Process │ │ Process │
│ LibWeb │ │ LibWeb │ │ LibWeb │
│ LibJS │ │ LibJS │ │ LibJS │
│ LibGfx │ │ LibGfx │ │ LibGfx │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└──────────────┼─────────────────┘
│ IPC
┌───────────┼───────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Request │ │ Image │ │ Web │
│ Server │ │ Decoder │ │ Worker │
│ (curl) │ │ (codecs) │ │ (JS VM) │
└──────────┘ └──────────┘ └──────────┘
Component Inventory
| Component | Path | Role |
|---|---|---|
| UI Process | UI/Qt/, UI/Gtk/, UI/AppKit/ |
Window management, tabs, user interaction |
| LibWebView | Libraries/LibWebView/ |
Browser framework: settings, bookmarks, cookies, history, process management |
| WebContent | Services/WebContent/ |
Page rendering, DOM, CSS, JS execution, painting |
| RequestServer | Services/RequestServer/ |
All network I/O (HTTP/HTTPS, WebSocket, DNS) via libcurl |
| ImageDecoder | Services/ImageDecoder/ |
Image format decoding in isolated process |
| WebWorker | Services/WebWorker/ |
Web Worker (dedicated + shared) execution |
| WebDriver | Services/WebDriver/ |
W3C WebDriver protocol server |
| LibJS | Libraries/LibJS/ |
ECMAScript engine (Rust parser + C++ bytecode VM) |
| LibWeb | Libraries/LibWeb/ |
Web platform: DOM, CSS, HTML, Layout, Painting, Fetch |
| LibGfx | Libraries/LibGfx/ |
Graphics primitives, image codecs, fonts, color profiles |
| LibCore | Libraries/LibCore/ |
Cross-platform OS abstraction (event loop, sockets, files, timers) |
| LibIPC | Libraries/LibIPC/ |
Inter-process communication framework |
| LibGC | Libraries/LibGC/ |
Garbage collector for JS heap |
| LibDNS | Libraries/LibDNS/ |
DNS resolver with DNSSEC support |
| LibCrypto | Libraries/LibCrypto/ |
ASN.1, X.509, cryptographic primitives |
| LibTLS | Libraries/LibTLS/ |
TLS 1.2 implementation |
| AK | AK/ |
Foundation library: strings, containers, smart pointers, utilities |
Technology Stack
| Layer | Technology |
|---|---|
| Language | C++23 (primary), Rust 2024 edition (parser, regex, unicode, gfx helpers) |
| Build | CMake 3.30+ with presets, vcpkg for dependencies, Ninja backend |
| Rendering | Skia (v144) with Vulkan (Linux/Windows/FreeBSD) or Metal (macOS) |
| Networking | libcurl (HTTP/1.1, HTTP/2, HTTP/3, WebSocket, brotli, zstd) |
| Media | FFmpeg 7.1.1 (H.264, VP8/9, AV1, Opus, Vorbis, WebP, Theora) |
| Fonts | HarfBuzz 10.2.0 (with ICU), fontconfig, freetype |
| Images | libjpeg-turbo, libpng (APNG), libjxl, libavif, libwebp, TIFF |
| Text | ICU 78.2, simdjson 4.2.4, simdutf 7.4.0 |
| Crypto | OpenSSL 3.5.3 |
| Database | SQLite 3.52.0 |
| Allocator | mimalloc 2.2.7 |
| UI | Qt 6.10.0, GTK 4 + libadwaita 1.8.4, AppKit (macOS native) |
| Debugging | cpptrace, libdwarf |
Data Flow: Page Load
- User enters URL in address bar → UI process sends
load_url()via IPC to WebContent - WebContent's
ResourceLoadersends fetch request via IPC to RequestServer - RequestServer resolves DNS (via
Resolver::default_resolver()or custom DNS-over-TLS), performs HTTP request via curl_multi - Response data streams back to WebContent via IPC
- WebContent parses HTML (LibWeb), constructs DOM, resolves CSS, runs JavaScript (LibJS)
- Image resources are dispatched to ImageDecoder processes for parallel decoding
- Layout tree is computed, display list generated, painting occurs via Skia
- Rendered bitmap is shared with UI process via anonymous shared memory buffers
- UI process composites and displays the result
2. System Requirements
Hardware
| Resource | Minimum | Recommended |
|---|---|---|
| CPU | x86_64 or AArch64, 2 cores | 4+ cores |
| RAM | 4 GB runtime, 8 GB build | 16 GB (build + browsing) |
| Disk | 10 GB (source + build + vcpkg) | 20 GB |
| GPU | Vulkan 1.0+ or Metal-capable | Vulkan 1.2+ / Apple Silicon |
Software
| Dependency | Required Version |
|---|---|
| C++ Compiler | Clang 18+ or GCC 14+ (C++23 support) |
| CMake | 3.30.0+ |
| Rust | 1.95.0 (pinned via rust-toolchain.toml) |
| Python | 3.8+ |
| Ninja | 1.11+ (recommended) |
| Git | 2.25+ |
| pkg-config | Any recent version |
Platform-Specific Requirements
| Platform | Graphics | Additional |
|---|---|---|
| Linux | Vulkan drivers, fontconfig, freetype, harfbuzz | D-Bus (for GTK), nasm, autoconf, automake, libtool |
| macOS | Metal (automatic) | Xcode Command Line Tools, nasm, autoconf, automake, libtool |
| Windows | Vulkan drivers, freetype | Visual Studio 2022 (C++ workload) |
| FreeBSD | Vulkan drivers, fontconfig, freetype, harfbuzz | nasm, autoconf, automake, libtool |
| Android | Vulkan | Android SDK/NDK (experimental) |
Note
The Rust toolchain is automatically managed via
rust-toolchain.toml. Componentsrustfmtandclippyare included. Code style: 120 max line width, 4-space tabs, Rust 2024 edition.
3. Configuration Reference
CMake Build Presets
Defined in CMakePresets.json (version 9 format, requires CMake 3.30+):
| Preset | Type | Description |
|---|---|---|
Release |
Build | Optimized production build |
Debug |
Build | Debug symbols, no optimization |
Sanitizer |
Build | AddressSanitizer + UBSan (Unix only) |
Platform-specific presets are included dynamically from Meta/CMake/presets/CMake${hostSystemName}Presets.json.
Environment Variables
| Variable | Type | Default | Description | Used By |
|---|---|---|---|---|
LADYBIRD_SOURCE_DIR |
string | (auto-set by test presets) | Path to the Ladybird source tree | Test runner |
MAKEJOBS |
int | CPU count | Number of parallel build jobs | Meta/ladybird.py |
BUILD_PRESET |
string | Release |
Default CMake build preset | Meta/ladybird.py |
CC |
string | (auto-detect) | C compiler path | Build system |
CXX |
string | (auto-detect) | C++ compiler path | Build system |
ASAN_OPTIONS |
string | strict_string_checks=1:check_initialization_order=1:strict_init_order=1:detect_stack_use_after_return=1:allocator_may_return_null=1 |
ASan configuration | Sanitizer preset |
UBSAN_OPTIONS |
string | print_stacktrace=1:print_summary=1:halt_on_error=1 |
UBSan configuration | Sanitizer preset |
Important
The Sanitizer preset's
ASAN_OPTIONSenablesdetect_stack_use_after_return=1, which significantly increases memory usage. Allocate at least 16 GB RAM when running sanitizer builds.
Complete CLI Flag Reference
All flags accepted by the Ladybird binary (from LibWebView::Application):
Positional arguments:
| Argument | Description |
|---|---|
url |
One or more URLs to open (optional) |
General options:
| Flag | Type | Default | Description |
|---|---|---|---|
--new-window / -n |
bool | false | Force opening in a new window |
--force-new-process |
bool | false | Start new process (disable SQL, partition cache) |
--allow-popups |
bool | false | Disable popup blocking |
--disable-scripting |
bool | false | Disable JavaScript |
--disable-sql-database |
bool | false | Disable SQLite database |
--tuple-file-origins |
bool | false | Treat file:// URLs as tuple origins |
--default-time-zone |
string | — | Override default time zone (e.g., America/New_York) |
--certificate / -C |
path | — | Path to additional certificate file |
--user-agent-preset |
string | — | Named User-Agent string preset |
--resource-map |
path | — | JSON file mapping URLs to local files |
Rendering options:
| Flag | Type | Default | Description |
|---|---|---|---|
--force-cpu-painting |
bool | false | Use Skia CPU backend instead of GPU |
--force-fontconfig |
bool | false | Force fontconfig for font loading |
--disable-scrollbar-painting |
bool | false | Don't paint viewport scrollbars |
Network options:
| Flag | Type | Default | Description |
|---|---|---|---|
--dns-server |
string | — | Custom DNS server address |
--dns-port |
int | 53 (853 with --dot) |
DNS server port |
--dot |
bool | false | Use DNS over TLS |
--dnssec |
bool | false | Validate DNSSEC locally |
--disable-http-memory-cache |
bool | false | Disable HTTP memory cache |
--disable-http-disk-cache |
bool | false | Disable HTTP disk cache |
--disable-content-filter |
bool | false | Disable content filter |
Headless mode:
| Flag | Type | Default | Description |
|---|---|---|---|
--headless |
string | screenshot |
Headless mode: screenshot, layout-tree, text, manual |
--screenshot-delay |
int | 0 | Seconds to wait before screenshot capture |
--window-width |
int | 800 | Viewport width in pixels |
--window-height |
int | 600 | Viewport height in pixels |
Developer / debug options:
| Flag | Type | Default | Description |
|---|---|---|---|
--devtools |
int | default port | Enable Firefox DevTools protocol server |
--debug-process |
string | — | Wait for debugger to attach to named process |
--profile-process |
string | — | Enable callgrind profiling for named process |
--test-mode |
bool | false | Enable test mode (experimental interfaces, internals, CPU painting) |
--log-all-js-exceptions |
bool | false | Log all JavaScript exceptions to console |
--disable-site-isolation |
bool | false | Disable site isolation |
--enable-idl-tracing |
bool | false | Enable Web IDL call tracing |
--expose-experimental-interfaces |
bool | false | Expose experimental Web API interfaces |
--expose-internals-object |
bool | false | Expose internals object for testing |
--enable-autoplay |
bool | false | Enable multimedia autoplay |
--collect-garbage-on-every-allocation / -g |
bool | false | Force GC after every JS heap allocation |
WebDriver options:
| Flag | Type | Description |
|---|---|---|
--webdriver-content-path |
path | WebDriver IPC socket path (non-macOS) |
--webdriver-mach-server-name |
string | WebDriver Mach server name (macOS only) |
about:settings WebUI Message Protocol
The settings page communicates with the browser via a WebUI messaging protocol:
| Message | Direction | Data |
|---|---|---|
loadSettings |
page → browser | Request current settings |
setBrowsingBehavior |
page → browser | { autoscroll: bool } |
setDefaultZoomLevelFactor |
page → browser | { factor: float } |
setLanguages |
page → browser | { languages: string[] } |
setDNSSettings |
page → browser | { type, server, port, dnssec } |
setNewTabPageURL |
page → browser | { url: string } |
setSiteSettingEnabledGlobally |
page → browser | { setting, enabled } |
addSiteSettingFilter |
page → browser | { setting, site, value } |
removeSiteSettingFilter |
page → browser | { setting, site } |
removeAllSiteSettingFilters |
page → browser | { setting } |
setGlobalPrivacyControl |
page → browser | { enabled: bool } |
setBrowsingDataSettings |
page → browser | { maxCacheSize } |
clearBrowsingData |
page → browser | { types[], timeRange } |
estimateBrowsingDataSizes |
page → browser | Request size estimates |
setSearchEngine |
page → browser | { engine: string } |
setAutocompleteEngine |
page → browser | { engine: string } |
addCustomSearchEngine |
page → browser | { name, url } |
removeCustomSearchEngine |
page → browser | { name } |
loadAvailableEngines |
page → browser | Request engine list |
loadForciblyEnabledSiteSettings |
page → browser | Request CLI-forced settings |
4. Deployment & Scaling
Build Configurations
Production (Release):
python3 Meta/ladybird.py build --preset Release
Produces optimized binaries with full LTO in the distribution Cargo profile. Rust libraries are built with panic = "abort", codegen-units = 1, and full LTO.
Debug:
python3 Meta/ladybird.py build --preset Debug
Includes debug symbols, assertions enabled, no optimization.
Sanitizer:
python3 Meta/ladybird.py build --preset Sanitizer
Caution
Sanitizer builds have 2-5x memory overhead and significant performance impact. Use for testing only, not production deployment.
vcpkg Dependency Management
All third-party dependencies are managed via vcpkg.json with pinned versions. Key dependencies and their versions:
| Dependency | Version | Purpose |
|---|---|---|
| Skia | 144 | 2D graphics rendering |
| curl | 8.19.0 | HTTP client (HTTP/2, HTTP/3, WebSocket) |
| FFmpeg | 7.1.1 | Media decoding |
| Qt | 6.10.0 | UI toolkit (optional) |
| libadwaita | 1.8.4 | GTK UI toolkit (optional) |
| HarfBuzz | 10.2.0 | Text shaping |
| ICU | 78.2 | Internationalization |
| OpenSSL | 3.5.3 | TLS/crypto |
| SQLite | 3.52.0 | Database storage |
| mimalloc | 2.2.7 | Memory allocator |
| simdjson | 4.2.4 | Fast JSON parsing |
| simdutf | 7.4.0 | Fast Unicode validation |
| SDL3 | 3.2.28 | Cross-platform multimedia |
vcpkg overlay ports live in Meta/CMake/vcpkg/overlay-ports/ for customized builds (ANGLE, curl, Skia, etc.).
Flatpak Packaging
After changing Rust dependencies, regenerate Flatpak sources:
python3 Meta/CMake/flatpak/generate-cargo-sources.py
Resource Limits
Ladybird automatically increases RLIMIT_NOFILE to 8192 on startup (non-Windows). This is required for the multi-process architecture's IPC socket usage. In containerized environments, ensure this limit is configurable.
5. Operations
Process Management
Each tab spawns a dedicated WebContent process. The UI process manages process lifecycles:
- WebContent processes — one per tab (or per site with site isolation). Spawned via
launch_web_content_process(). Spare processes can be pre-launched withlaunch_spare_web_content_process(). - RequestServer — shared network process. New clients connect via
connect_new_request_server_client(). - ImageDecoder — shared image decoding process with multi-client support via
connect_new_clients(count). - WebWorker — spawned per Web Worker (dedicated or shared).
Tip
Use
--debug-process WebContentto pause a WebContent process at startup and attach a debugger. This is invaluable for diagnosing rendering bugs.
Disk Cache
RequestServer maintains an HTTP disk cache at {cache_dir}/Ladybird/:
| File | Purpose |
|---|---|
alt-svc-cache.txt |
HTTP Alt-Svc header cache |
| HTTP disk cache files | Cached HTTP responses |
Cache behavior is controlled by:
--disable-http-disk-cache— disable entirely--force-new-process— enables partitioned disk caching (HTTPDiskCacheMode::Partitioned)about:settings→ Privacy → disk cache size configuration
Note
When
--force-new-processis used, the SQL database is disabled and disk caching switches to partitioned mode to prevent data leakage between instances.
Monitoring
Ladybird does not expose a metrics endpoint. Monitor process health via:
- Process table: check for
WebContent,RequestServer,ImageDecoder,WebWorkerprocesses - File descriptor usage:
ls /proc/<pid>/fd | wc -l - Memory: each WebContent process runs an independent JS heap with garbage collection
Debug Requests
WebContent processes support debug introspection via debug_request(page_id, request, argument):
| Request | Description |
|---|---|
dump-dom-tree |
Print DOM tree to stdout |
dump-layout-tree |
Print layout tree |
dump-paint-tree |
Print paint tree |
dump-stacking-context-tree |
Print stacking context hierarchy |
dump-style-sheets |
Print all loaded CSS style sheets |
dump-all-resolved-styles |
Print computed styles for all elements |
dump-all-css-errors |
Print CSS parsing errors |
dump-display-list |
Print the display list |
dump-session-history |
Print navigation session history |
collect-garbage |
Trigger deferred garbage collection |
set-line-box-borders |
Toggle line box border visualization |
6. Performance
Rendering Pipeline
- DOM Construction — HTML is parsed into a DOM tree
- Style Resolution — CSS rules are matched and computed properties calculated (
StyleComputer) - Layout — box tree construction, formatting contexts (block, inline, flex, grid)
- Painting — display list generation, stacking context ordering
- Compositing — Skia renders the display list to a GPU (Vulkan/Metal) or CPU bitmap
- Transfer — rendered bitmap shared with UI process via anonymous shared memory (
AnonymousBuffer)
Input Event Coalescing
WebContent implements mouse event coalescing: consecutive mousemove and wheel events are merged when the pipeline is busy, reducing IPC overhead and improving responsiveness.
JavaScript Performance
LibJS uses a three-tier execution strategy:
- Rust Parser — tokenizes and parses source into AST
- Bytecode Compiler — generates bytecode from AST
- AsmIntGen Interpreter — assembly-level threaded-dispatch interpreter for x86_64 and AArch64
The AsmIntGen interpreter maps bytecode opcodes to native assembly handlers via a 256-entry dispatch table. Five registers are pinned to callee-saved hardware registers for performance:
| Register | Purpose |
|---|---|
pc |
Program counter (byte offset into bytecode) |
pb |
Bytecode base pointer |
values |
Pointer to Value[] array |
exec_ctx |
Running ExecutionContext pointer |
dispatch |
Dispatch table base pointer |
NaN-boxing is used for JS values: the upper 16 bits encode the value type tag, allowing efficient type checks and unboxing.
Image Decoding
ImageDecoder uses streaming decoding for animated images with a batch size of 4 frames, keeping the decoder alive via AnimationSession for future frame requests. This avoids re-parsing the entire image for each frame.
Tip
For debugging rendering performance, use
--force-cpu-paintingto bypass GPU acceleration and isolate whether performance issues are in the rendering pipeline or GPU driver.
7. Security
Process Isolation
Each WebContent process runs in its own address space. Site isolation (enabled by default) ensures that content from different origins runs in separate processes, preventing cross-site data leakage through memory vulnerabilities.
| Isolation feature | Status |
|---|---|
| Per-tab process isolation | Enabled |
| Site isolation | Enabled (disable with --disable-site-isolation) |
| Network process isolation | Enabled (RequestServer is a separate process) |
| Image decoder isolation | Enabled (ImageDecoder is a separate process) |
Warning
Disabling site isolation (
--disable-site-isolation) removes a critical security boundary. It is automatically disabled when debugging WebContent processes.
IPC Security
IPC uses Unix domain sockets (Linux/FreeBSD/Windows) or Mach ports (macOS). On macOS, Mach port names are derived from the process name and PID (mach_server_name_for_process("Ladybird", pid)), scoping IPC to the running instance.
Cryptographic Support
LibCrypto supports the following algorithms (from ASN.1 OID constants):
| Algorithm | OIDs |
|---|---|
| RSA | RSA-MD5, RSA-SHA1, RSA-SHA224, RSA-SHA256, RSA-SHA384, RSA-SHA512 |
| ECDSA | ECDSA-SHA224, ECDSA-SHA256, ECDSA-SHA384, ECDSA-SHA512 |
| Curves | secp256r1, secp384r1, secp521r1, X25519, X448 |
| EdDSA | Ed25519, Ed448 |
| Post-quantum | ML-DSA-44, ML-DSA-65, ML-DSA-87, ML-KEM-512, ML-KEM-768, ML-KEM-1024 |
TLS
LibTLS implements TLS 1.2. OpenSSL 3.5.3 is available via vcpkg for additional TLS support in libcurl.
DNS Security
- DNS-over-TLS — encrypts DNS queries, configurable via
--dotorabout:settings - DNSSEC — local validation available via
--dnssecorabout:settings - DNS resolution runs in the isolated RequestServer process
Global Privacy Control
When enabled in about:settings, the browser sends Sec-GPC: 1 on all HTTP requests, signaling the user's preference not to have their data sold or shared.
Content Filtering
A content filter (ContentFilter) is available and enabled by default. Disable with --disable-content-filter.
Known Security Issues
GitHub issue #9062 reports a use-after-free read/write via stale TypedArray cached_data_ptr on shared WebAssembly.Memory after multiple grows. This is an active vulnerability in the WebAssembly implementation.
Caution
The UAF in shared WebAssembly.Memory (#9062) could allow memory corruption. Monitor the issue tracker for the fix.
8. Developer Internals
8.1 Repository Structure
ladybird/
├── AK/ # Foundation library (196 files, 1.3M chars)
│ ├── String.h/cpp # UTF-8 string types
│ ├── HashMap.h # Hash map container
│ ├── Vector.h # Dynamic array
│ ├── Optional.h # Optional<T> type
│ ├── Error.h # ErrorOr<T> error handling
│ └── ... # Smart pointers, formatting, math, etc.
├── Libraries/
│ ├── LibWeb/ # Web platform (DOM, CSS, HTML, Layout, Painting, Fetch)
│ ├── LibJS/ # JavaScript engine
│ │ ├── Rust/src/ # Rust parser (lexer, parser, scope collector, AST)
│ │ ├── AsmIntGen/src/ # Assembly interpreter code generator
│ │ ├── BytecodeDef/src/ # Bytecode definition DSL
│ │ └── *.cpp/h # C++ runtime, builtins, GC integration
│ ├── LibWebView/ # Browser framework (65 files, 473K chars)
│ ├── LibCore/ # OS abstraction (97 files, 395K chars)
│ ├── LibGfx/ # Graphics (93 files, 425K chars)
│ ├── LibIPC/ # IPC framework (37 files, 125K chars)
│ ├── LibGC/ # Garbage collector (34 files, 105K chars)
│ ├── LibDNS/ # DNS resolver (3 files, 134K chars)
│ ├── LibUnicode/ # ICU integration (40 files, 246K chars)
│ ├── LibCrypto/ # Cryptographic primitives
│ ├── LibTLS/ # TLS 1.2 implementation
│ ├── LibHTTP/ # HTTP cache
│ ├── LibRequests/ # Network request types
│ ├── LibWebSocket/ # WebSocket support
│ ├── LibWasm/ # WebAssembly engine
│ ├── LibLine/ # Terminal line editor (REPL)
│ ├── LibRegex/ # Regular expression engine (with Rust)
│ ├── LibCompress/ # Compression (zlib, brotli, zstd)
│ ├── LibTextCodec/ # Text encoding/decoding
│ ├── LibXML/ # XML parser
│ ├── LibIDL/ # Web IDL parser
│ ├── LibDatabase/ # SQLite wrapper
│ ├── LibDiff/ # Diff algorithm
│ ├── LibSyntax/ # Syntax highlighting
│ ├── LibTest/ # Testing framework
│ ├── LibMedia/ # Media codec integration
│ ├── LibDevTools/ # Firefox DevTools protocol
│ ├── LibImageDecoderClient/ # ImageDecoder IPC client
│ ├── LibFileSystem/ # File system utilities
│ ├── LibThreading/ # Threading primitives
│ ├── LibMain/ # Entry point helpers
│ └── LibURL/ # URL parsing
├── Services/
│ ├── WebContent/ # Page rendering process (18 files, 319K chars)
│ ├── RequestServer/ # Network I/O process (18 files, 93K chars)
│ ├── ImageDecoder/ # Image decoding process (4 files, 16K chars)
│ ├── WebWorker/ # Web Worker process (8 files, 38K chars)
│ └── WebDriver/ # WebDriver server (7 files, 78K chars)
├── UI/
│ ├── Qt/ # Qt 6 UI (33 files, 231K chars)
│ ├── Gtk/ # GTK 4 UI (18 files, 115K chars)
│ ├── AppKit/ # macOS native UI
│ ├── Android/ # Android UI (experimental)
│ └── Icons/ # Application icons
├── Utilities/
│ ├── js.cpp # JavaScript REPL
│ ├── dns.cpp # DNS query tool
│ └── image.cpp # Image format converter
├── Meta/
│ ├── ladybird.py # Build script
│ ├── WPT.sh # Web Platform Tests runner
│ ├── CMake/ # CMake modules, vcpkg config
│ ├── Generators/ # Code generators (Python)
│ ├── Linters/ # Code linters
│ ├── Lagom/ # Lagom build framework
│ └── Utils/ # Build utilities
├── Tests/ # Test suites per library
├── Base/res/ # Resources (icons, about pages, templates, fonts)
├── Documentation/ # Build instructions, coding style, architecture docs
├── CMakePresets.json # Build presets
├── Cargo.toml # Rust workspace
├── rust-toolchain.toml # Pinned Rust version (1.95.0)
├── vcpkg.json # Dependency manifest
└── .pre-commit-config.yaml # Pre-commit hooks
8.2 AK Foundation Library
AK (formerly "AK" from SerenityOS) provides the foundational types used throughout the codebase:
String types:
| Type | Encoding | Allocation | Use case |
|---|---|---|---|
String |
UTF-8 | Heap | General-purpose strings |
ByteString |
raw bytes | Heap | Binary data, legacy code |
FlyString |
UTF-8 | Interned | Frequently-compared strings |
StringView |
UTF-8 | Borrowed | Non-owning reference |
Utf16String |
UTF-16 | Heap | JavaScript string storage |
Utf16FlyString |
UTF-16 | Interned | JavaScript interned strings |
Smart pointers:
| Type | Semantics |
|---|---|
NonnullRefPtr<T> |
Non-null reference-counted |
RefPtr<T> |
Nullable reference-counted |
NonnullOwnPtr<T> |
Non-null unique ownership |
OwnPtr<T> |
Nullable unique ownership |
GC::Root<T> |
GC root preventing collection |
GC::Ref<T> |
GC-managed reference |
Containers: Vector<T>, HashMap<K,V>, HashTable<T>, Array<T,N>, Queue<T>, CircularBuffer, SinglyLinkedList<T>.
Error handling: ErrorOr<T> return type with TRY() macro for propagation. ThrowCompletionOr<Value> for JS-visible errors.
8.3 IPC Framework
All inter-process communication is built on IPC::ConnectionFromClient<ClientEndpoint, ServerEndpoint>:
- Endpoint IDL files define the message protocol (serialization code generated at build time)
- Transport layer uses Unix domain sockets (Linux/FreeBSD/Windows) or Mach ports (macOS)
- Connection lifecycle: UI process spawns child, establishes IPC transport, passes handles for cross-service connections
UI Process
│
├── IPC::Transport ──→ WebContent (per tab)
│ │
│ ├── passes IPC handle ──→ RequestServer
│ └── passes IPC handle ──→ ImageDecoder
│
└── IPC::Transport ──→ WebDriver
On macOS, IPC::MachBootstrapListener and IPC::TransportBootstrapMach handle Mach port bootstrapping. The server name format is Ladybird-<pid>.
8.4 Event Loop Architecture
Each UI backend provides its own event loop bridge:
| Backend | EventLoop Implementation | Native Loop |
|---|---|---|
| Qt | EventLoopImplementationQt / EventLoopManagerQt |
QApplication::exec() |
| GTK | EventLoopImplementationGtk / EventLoopManagerGtk |
GLib main loop |
| AppKit | AppKit run loop | NSRunLoop |
These bridge Core::EventLoop (the platform-agnostic event loop) to the native toolkit's event loop, enabling timers, socket notifications, and deferred invocations to work seamlessly across UI backends.
8.5 Garbage Collection
LibGC implements a mark-and-sweep garbage collector for the JavaScript heap:
GC::Heap— manages allocationsGC_DEFINE_ALLOCATORmacro — declares allocator for a GC-managed typeGC::Root<T>— prevents an object from being collectedGC::RootVector<T>— a vector of GC rootsvisit_edges()method — each GC-managed object implements this to mark reachable references
The --collect-garbage-on-every-allocation / -g flag triggers GC after every allocation, useful for detecting GC-related bugs.
8.6 LibJS Engine Architecture
Parser (Rust):
Lexer(lexer.rs) — tokenizer with save/restore state, regex disambiguationParser(parser.rs) —parse_program(starts_in_strict_mode)returns ASTScopeCollector(scope_collector.rs) — variable binding analysis with scope types: program, function, block, for-loop, with, catch, static-init, class-field, class-declarationAST(ast.rs) — comprehensive node types for all ECMAScript constructs- FFI:
rust_tokenize()provides token stream to C++ (used by REPL syntax highlighting)
Bytecode Compiler (C++):
Compiles AST to a bytecode format defined in Libraries/LibJS/BytecodeDef/:
- Opcodes are defined in a DSL (
Bytecode.def) generate_libjs_bytecode_def_derived.pygenerates C++ from the definition- Fields include operand types, sizes, and layout information
AsmIntGen (Rust → Assembly):
Compiles a platform-neutral DSL (asmint.asm) into native assembly for the bytecode interpreter's hot loop:
- Targets: x86_64 (
codegen_x86_64.rs) and AArch64 (codegen_aarch64.rs) - 256-entry dispatch table maps opcodes to handler addresses
- Threaded dispatch: each handler executes one instruction, reads next opcode, jumps through table
- C++ interop:
call_slow_path(terminal),call_helper(non-terminal),call_interp(non-terminal),call_raw_native(indirect function pointer) - NaN-boxing operations:
extract_tag,unbox_int32,unbox_object,box_int32,box_int32_clean - 32-bit overflow-checked arithmetic:
add32_overflow,sub32_overflow,mul32_overflow,neg32_overflow
Console implementation follows the WHATWG Console spec: assert_, clear, debug, error, info, log, table, trace, warn, dir, count, countReset, group, groupCollapsed, groupEnd, time, timeLog, timeEnd.
8.7 LibWeb Architecture
LibWeb implements the web platform across multiple namespaces:
| Namespace | Subsystem | Key Classes |
|---|---|---|
Web::DOM |
Document Object Model | Document, Element, Text, ShadowRoot, Attr, ParentNode |
Web::CSS |
Cascading Style Sheets | StyleComputer, CSSStyleSheet, ComputedProperties, PropertyID |
Web::HTML |
HTML elements & APIs | Window, BrowsingContext, TraversableNavigable, Storage |
Web::Layout |
Box model & formatting | Viewport, BlockContainer, InlineNode, FormattingContext |
Web::Painting |
Rendering pipeline | PaintableBox, ViewportPaintable, StackingContext |
Web::Fetch |
Fetch API | Fetching |
Web::WebDriver |
WebDriver protocol | Client, Session |
Web::IndexedDB |
IndexedDB storage | Internal implementation |
Web::Editing |
Content editing | Internal editing commands |
8.8 Code Generation Pipeline
Ladybird uses extensive code generation for Web API bindings and browser internals:
Python generators (Meta/Generators/):
| Generator | Input | Output |
|---|---|---|
generate_ipc_definitions.py |
IPC endpoint .ipc files |
C++ serialization/deserialization code |
generate_dom_tree.py |
HTML templates | C++ static DOM tree builders |
embed_as_string.py |
Any file | C++ raw string literal variable |
generate_encoding_indexes.py |
JSON encoding data | C++ encoding lookup tables |
generate_public_suffix_data.py |
Public suffix list | C++ public suffix lookup |
generate_libwasm_spec_test.py |
Wasm spec test JSON | C++ test cases |
libjs_bytecode_def.py |
Bytecode.def |
C++ bytecode definitions |
C++ generators (Meta/Lagom/Tools/CodeGenerators/LibWeb/):
| Generator | Purpose |
|---|---|
GenerateCSSPropertyID |
CSS property enum and metadata |
GenerateCSSEnums |
CSS enum types |
GenerateCSSKeyword |
CSS keyword definitions |
GenerateCSSPseudoClass |
CSS pseudo-class handling |
GenerateCSSPseudoElement |
CSS pseudo-element handling |
GenerateCSSDescriptors |
CSS descriptor definitions |
GenerateCSSUnits |
CSS unit types |
GenerateCSSTransformFunctions |
CSS transform functions |
GenerateCSSMathFunctions |
CSS math functions |
GenerateCSSStyleProperties |
CSS style property accessors |
GenerateCSSMediaFeatureID |
CSS media feature IDs |
GenerateCSSNumericFactoryMethods |
CSS numeric type factories |
GenerateCSSEnvironmentVariable |
CSS env() variable support |
GenerateAriaRoles |
ARIA role definitions |
GenerateNamedCharacterReferences |
HTML named character references |
GenerateWindowOrWorkerInterfaces |
Web IDL interface registration |
BindingsGenerator |
Web IDL → C++ binding code |
8.9 Testing Infrastructure
Test suites per library:
| Suite | Framework | Files |
|---|---|---|
Tests/AK/ |
LibTest | 87 files, 838K chars |
Tests/LibJS/ |
LibTest + custom runners | JS runtime, AST, bytecode tests |
Tests/LibWeb/ |
LibTest + WPT | Layout, text, ref, crash tests |
Tests/LibCore/ |
LibTest | 12 files |
Tests/LibCrypto/ |
LibTest | 16 files, 247K chars |
Tests/LibGfx/ |
LibTest | Image codec tests |
Tests/LibRegex/ |
LibTest | 1 file, 26K chars |
Tests/LibURL/ |
LibTest | 4 files, 41K chars |
Tests/LibUnicode/ |
LibTest | 7 files |
Tests/LibWasm/ |
LibTest | Wasm spec tests |
Tests/LibIPC/ |
LibTest | IPC serialization tests |
Tests/LibDNS/ |
LibTest | DNS resolver tests |
Tests/LibTLS/ |
LibTest | TLS protocol tests |
Tests/LibDatabase/ |
LibTest | SQLite wrapper tests |
Tests/LibCompress/ |
LibTest | Compression tests |
Tests/LibHTTP/ |
LibTest | HTTP cache tests |
Tests/LibWebView/ |
LibTest | Browser framework tests |
Tests/ClangPlugins/ |
lit (LLVM) | Clang plugin tests |
LibTest framework features:
RANDOMIZED_TEST_CASE— property-based testing with automatic shrinking- Generator combinators:
Gen::unsigned_int(max),Gen::vector(min, max, gen) ASSUME(predicate)— reject invalid test inputs- Automatic shrinking to minimal failing example
Web Platform Tests (Meta/WPT.sh):
| Command | Description |
|---|---|
./Meta/WPT.sh update |
Clone/update WPT repository |
./Meta/WPT.sh run [tests] |
Run tests (auto-builds Ladybird + WebDriver) |
./Meta/WPT.sh compare <log> [tests] |
Run and compare against baseline |
./Meta/WPT.sh import [paths] |
Import tests from wpt.live |
./Meta/WPT.sh list-tests [paths] |
List available tests |
./Meta/WPT.sh clean |
Cleanup overlayfs mounts and network namespaces |
./Meta/WPT.sh bisect <bad> <good> [tests] |
Git bisect for test regressions |
WPT execution options: --show-window, --debug-process <name>, --parallel-instances <N>, various log formats (raw, xunit, html, mach, wptreport, wptscreenshot, chromium).
Linux parallel execution uses network namespaces (ip netns) with overlayfs for per-instance WPT directories.
Running tests:
# Run all tests
python3 Meta/ladybird.py test
# Run specific test binary
./Build/Release/bin/TestLibJS
# Run WPT tests
./Meta/WPT.sh run css/css-flexbox/
Note
Test presets automatically set
LADYBIRD_SOURCE_DIRand sanitizer environment variables. Always use test presets rather than running test binaries directly to ensure proper configuration.
8.10 Utility Programs
js — JavaScript REPL and script runner (Utilities/js.cpp):
| Feature | Details |
|---|---|
| Interactive REPL | LibLine::Editor with syntax highlighting (Unix only) |
| Script execution | Files run as scripts or modules (--as-module) |
| REPL functions | exit(code), help(), save(file), loadINI(file), loadJSON(file), print(value) |
| Last value | _ accesses the last evaluated result |
| Flags | --dump-ast, --as-module, --strip-ansi, --raw-strings, --disable-source-location-hints |
| History | Saved to persistent history file |
| Signal handling | Ctrl+C: first press warns, second press exits |
dns — DNS query tool (Utilities/dns.cpp):
# Query A and AAAA records
./Build/Release/bin/dns A,[email removed]
# DNS-over-TLS with DNSSEC
./Build/Release/bin/dns --tls --server 1.1.1.1 --dnssec [email removed]
# With custom CA certificates
./Build/Release/bin/dns --ca-certs /path/to/certs.pem --tls [email removed]
image — Image format converter (Utilities/image.cpp):
# Convert PNG to WebP
./Build/Release/bin/image input.png -o output.webp
# Convert with JPEG quality setting
./Build/Release/bin/image input.png -o output.jpg --quality 85
# Crop and convert
./Build/Release/bin/image input.png -o output.bmp --crop 10,20,200,300
# Benchmark decoding (no output)
./Build/Release/bin/image input.avif --no-output
Options: -o <path>, --no-output, --frame-index <N>, --invert-cmyk, --crop <x,y,w,h>, --move-alpha-to-rgb, --strip-alpha, --assign-color-profile <file>, --quality <0-100>, --webp-allowed-transforms <list>.
8.11 Linters (Meta/Linters/)
| Linter | File | Purpose |
|---|---|---|
lint_clang_format.py |
C++ formatting | Runs clang-format with --overwrite-inplace option |
check_idl_files.py |
Web IDL files | Validates IDL file correctness |
lint_ipc.py |
IPC definitions | Validates IPC endpoint definitions |
lint-ci.sh |
CI integration | Pre-commit hook runner |
lint-commit.sh |
Commit messages | Commit message format validation |
8.12 Clang Plugins (Meta/Lagom/ClangPlugins/)
Two custom Clang plugins enforce code correctness at compile time:
| Plugin | Purpose |
|---|---|
LambdaCaptureClangPlugin |
Validates lambda capture correctness |
LibJSGCClangPlugin |
Detects GC-unsafe patterns in LibJS code |
The GC scanner plugin optionally detects invalid function members (-fplugin-arg-libjs_gc_scanner-detect-invalid-function-members).
8.13 Fuzz Testing (Meta/Lagom/Fuzzers/)
36 fuzzer targets cover critical attack surfaces:
Areas fuzzed include: compression formats (brotli, deflate, gzip, lzma, zlib), image decoders (AVIF, BMP, GIF, ICO, ILBM, JPEG, JPEG2000, JXL, PNG, QOI, TIFF, TGA, WebP), font parsers (OpenType, WOFF/WOFF2), text processing (regex, URL parsing, text decoders), web formats (CSS, HTML, HTTP, XML), cryptographic protocols (TLS), and WebAssembly.
9. Integration Points
WebDriver (W3C Protocol)
Ladybird implements the W3C WebDriver standard for browser automation:
Starting the server:
./Build/Release/bin/WebDriver --port 4444
Supported endpoints:
| Method | Path | Description |
|---|---|---|
POST |
/session |
Create new session |
DELETE |
/session/{id} |
Delete session |
GET |
/status |
Server readiness state |
GET/POST |
/session/{id}/timeouts |
Get/set timeouts |
POST |
/session/{id}/url |
Navigate to URL |
GET |
/session/{id}/url |
Get current URL |
POST |
/session/{id}/back |
Navigate back |
POST |
/session/{id}/forward |
Navigate forward |
Session capabilities are processed per W3C specification. SessionFlags::Http is used for HTTP sessions.
Important
Only one HTTP WebDriver session is supported at a time. Concurrent sessions will fail with a session creation error.
Firefox DevTools Protocol
LibDevTools implements the Firefox DevTools protocol server, enabling remote debugging:
Ladybird --devtools 9222
Connect via Firefox's "Remote Debugging" feature or any compatible client. Supports DOM inspection, CSS editing, and JavaScript debugging.
D-Bus Integration (GTK)
The GTK backend registers as org.ladybird.Ladybird on the D-Bus session bus. Features:
- Single-instance activation: opening a URL when Ladybird is already running sends the URL to the existing instance
- Signal handling:
openandactivatesignals for URL forwarding forward_urls_to_remote_and_exit()detects an existing instance and forwards URLs instead of launching a new process
Mach Port IPC (macOS)
On macOS, IPC uses Mach ports instead of Unix sockets:
IPC::MachBootstrapListener— listens for incoming Mach port connectionsIPC::TransportBootstrapMach— establishes Mach transport- Server names:
Ladybird-<pid>format viamach_server_name_for_process() - WebDriver uses
--webdriver-mach-server-nameinstead of--webdriver-content-path
10. Troubleshooting
Build Failures
Error: CMake version too old
Cause: CMake < 3.30.0 does not support the preset format used.
Solution: Install CMake 3.30.0+ from https://cmake.org or your package manager's latest repository.
Error: Rust compilation failures
Cause: Wrong Rust version or missing components.
Solution:
- Ensure
rust-toolchain.tomlis present (pinned to 1.95.0) - Run
rustup showto verify the active toolchain - If needed:
rustup install 1.95.0 && rustup component add rustfmt clippy
Error: vcpkg build failures in overlay ports
Cause: Stale vcpkg cache or network issues.
Solution:
- Delete vcpkg build cache:
rm -rf Build/Release/vcpkg_installed - Retry the build
- Check proxy settings if behind a corporate firewall
Warning
Deleting
vcpkg_installedforces a full dependency rebuild, which can take 30+ minutes.
Runtime Issues
WebContent process crashes repeatedly
Cause: Rendering engine bug, GPU driver incompatibility, or memory corruption.
Solution:
- Try
--force-cpu-paintingto rule out GPU issues - Check
dmesgor system logs for OOM killer activity - Run with
--debug-process WebContentand attach a debugger - Build with Sanitizer preset for memory error detection
RequestServer DNS resolution fails
Cause: DNS server unreachable or misconfigured.
Solution:
- Test DNS directly:
./Build/Release/bin/dns [email removed] - Try system DNS: remove
--dns-serverflag - Check firewall rules for DNS ports (53/UDP, 853/TCP for DoT)
High memory usage with many tabs
Cause: Each tab runs a separate WebContent process with its own memory space.
Solution:
- Close unused tabs
- Monitor per-process memory:
ps aux | grep WebContent - Use
--collect-garbage-on-every-allocation(debug only) to check for leaks
Note
Memory usage scales linearly with open tabs since each tab has its own process, JS heap, and DOM tree. This is by design for security isolation.