Lightpanda Browser — Feature Gap & Improvement Report
AI-assisted audit (Claude Sonnet 4.6) — April 5, 2026 — codebase: Zig + V8 + html5ever
Lightpanda is a headless browser engine written in Zig that integrates V8 for JavaScript execution and implements Web APIs from scratch. It targets AI agents and browser automation, claiming 16× less RAM and 9× faster startup than Chrome. The engine is functionally solid for DOM manipulation, JavaScript execution, CSS selector matching, and HTTP networking — the core use cases of headless automation.
The codebase contains 215+ WebAPI files, 71 HTML element implementations, and 334 HTML test files. A scan reveals ~88 TODO/FIXME markers across 43 files, numerous stub/noop method implementations, and several Web APIs that are either absent or skeletal.
1. Critical Missing Features
These gaps either break real-world scripts or block entire categories of automation use cases.
1.1 Layout Engine / Visual Metrics
Status: Stub — returns zeros everywhere
File: src/browser/webapi/element/Element.zig
No layout engine exists. All geometric queries return empty/zero results:
| API | Current behavior |
|---|---|
getBoundingClientRect() | Returns {0, 0, 0, 0, 0, 0, 0, 0} |
getClientRects() | Returns empty DOMRectList |
getComputedStyle() | Not implemented |
offsetWidth / offsetHeight | Not implemented |
offsetParent | Not implemented |
scrollWidth / scrollHeight | Not implemented |
clientWidth / clientHeight | Not implemented |
Impact: High. Many automation scripts check element dimensions for visibility detection, scroll position, and interaction targeting. Libraries like HTMX, Alpine.js, and virtually all UI frameworks call getBoundingClientRect().
What could be done: A simplified block layout pass (no float, no flex) that computes approximate box dimensions from CSS width/height/display properties would satisfy the majority of automation use cases without a full layout engine.
1.2 CORS Enforcement
Status: Not enforced (open issue #2015)
File: src/browser/webapi/fetch/Fetch.zig
All cross-origin requests are treated as mode "cors" for labeling purposes, but no preflight is issued, no Access-Control-Allow-Origin header is validated, and no request is blocked. Scripts that intentionally rely on CORS failures will silently succeed. CORS-dependent auth flows may bypass security checks.
1.3 Web Workers
Status: Absent
No Web Workers, Shared Workers, or Service Workers are implemented. The server uses internal Zig threads but none are exposed to the JS runtime. navigator.serviceWorker returning undefined causes hard failures in PWAs.
What could be done: A minimal Web Worker implementation running a JS bundle in a separate V8 isolate and communicating via postMessage would cover the majority of use cases.
1.4 IndexedDB
Status: Absent
window.indexedDB is not defined. A large fraction of modern web apps use IndexedDB for client-side state, causing hard JS errors on page load.
1.5 File API
Status: Completely stubbed
File: src/browser/webapi/file/File.zig
The File interface has a comment: // TODO: Implement File API. The constructor creates a Blob, not a real File object. FileReader is mostly noop. File upload forms and drag-and-drop automations cannot work.
1.6 Cache API
Status: Absent
window.caches / CacheStorage / Cache are not present. Service Worker integration depends on this.
2. Partially Implemented APIs
These APIs exist in the codebase but have significant gaps marked by TODOs, noop methods, or stub returns.
2.1 Canvas 2D Context
Files: src/browser/webapi/canvas/OffscreenCanvasRenderingContext2D.zig, Canvas.zig
OffscreenCanvasRenderingContext2D has dozens of noop methods: save, restore, scale, rotate, translate, transform, setTransform, resetTransform, clearRect, fillRect, strokeRect, beginPath, closePath, moveTo, lineTo, quadraticCurveTo, bezierCurveTo, arc, arcTo, rect, fill, stroke, clip, fillText, putImageData.
getImageData() returns a DOMException. OffscreenCanvas.transferToImageBitmap() returns null because ImageBitmap is not implemented. Inline canvas also missing CanvasGradient and CanvasPattern.
2.2 WebGL
File: src/browser/webapi/canvas/WebGLRenderingContext.zig
getParameter() returns an empty string for all queries. getSupportedExtensions() is a stub empty array. Only extension returned is WEBGL_lose_context with noop methods. No shader compilation, buffer management, or draw calls.
2.3 Animation API
File: src/browser/webapi/animation/Animation.zig
Animations simulate a fake lifecycle: idle → running → finished. The effect and timeline constructor parameters are explicitly ignored (TODO in source). No actual timing, keyframe interpolation, or composite operations.
2.4 Performance Timing
File: src/browser/webapi/performance/Performance.zig
performance.timing returns 0 for all navigation timing properties. performance.navigation returns hard-coded values. Scripts that gate on performance.timing.loadEventEnd > 0 will hang indefinitely.
2.5 SubtleCrypto
Files: src/browser/webapi/SubtleCrypto.zig, src/browser/webapi/crypto/
Implemented algorithms: HMAC, X25519. Missing: RSA-OAEP, AES-GCM, AES-CBC, AES-CTR, ECDSA, ECDH, SHA-based KDFs. pkcs8 key format is explicitly marked unsupported. Any authentication library using RSA/AES/ECDSA operations will throw.
2.6 XMLHttpRequest
File: src/browser/webapi/xhr/XMLHttpRequest.zig
Only text and json response types supported; arraybuffer, blob, document missing. URL parameter accepts only strings; spec allows URL objects. Optional withCredentials and timeout handling incomplete.
2.7 Streams API
Files: src/browser/webapi/streams/, encoding/TextEncoderStream.zig, TextDecoderStream.zig
ReadableStream, WritableStream, TransformStream implementations exist but streaming encoding/decoding has a TODO: "handle encoding properly". Backpressure and byte stream modes are unverified for spec compliance.
2.8 Other Partial APIs
| API | Gap | File |
|---|---|---|
| FontFaceSet | Events fire before font is parsed (TODO) | FontFaceSet.zig |
| Selection | getComposedRanges() unimplemented | Selection.zig |
| AbstractRange / StaticRange | Missing static_range field (TODO); StaticRange absent | AbstractRange.zig |
| InputEvent | dataTransfer not implemented (TODO) | event/InputEvent.zig |
| UIEvent | sourceCapabilities not implemented | event/UIEvent.zig |
| PerformanceObserver | buffered option ignored (TODO) | PerformanceObserver.zig |
| MediaElement (Audio/Video) | API surface present, no media pipeline | element/html/Media.zig |
3. CDP Protocol Gaps
3.1 Emulation Domain
File: src/cdp/domains/emulation.zig
Four methods are noop:
| Method | Status |
|---|---|
setEmulatedMedia | noop |
setFocusEmulationEnabled | noop |
setDeviceMetricsOverride | noop |
setTouchEmulationEnabled | noop |
Playwright's device emulation (page.emulate(devices['iPhone 14'])) silently has no effect. Tests that verify mobile rendering will pass without actually testing mobile behavior.
3.2 Other CDP Gaps
| Domain | Gap | File |
|---|---|---|
| Browser | 5 noop methods, hard-coded version/window data | domains/browser.zig |
| Network | Missing requestWillBeSentExtraInfo, responseReceivedExtraInfo | domains/network.zig |
| DOM | Quad computation incomplete for certain CSS | domains/dom.zig |
| Page | transitionType and referrerPolicy not typed to enum | domains/page.zig |
| Fetch | Origin determination for proxied requests is TODO | domains/fetch.zig |
4. CSS Engine Gaps
| Feature | Status | File |
|---|---|---|
| CSS URL parser | Simplified — TODO: "Need a true URL parser here" | css/Tokenizer.zig |
@supports rule | Not parsed — blocks gated behind it may be applied unconditionally | css/Parser.zig |
:placeholder-shown | Defined in selector list but match returns false | selector/Selector.zig |
getComputedStyle() | Not implemented at DOM level; only inline styles accessible | element/Element.zig |
@import | No support for CSS @import chaining | — |
@layer | Cascade layers absent | — |
5. Absent Web APIs
APIs with zero implementation:
| API | Spec | Automation Priority |
|---|---|---|
| ImageBitmap | HTML Living Standard | Medium — blocks Canvas pipeline |
| Broadcast Channel | HTML Living Standard | Medium — inter-tab messaging |
| Geolocation | W3C | Medium — some sites gate on it |
| Clipboard API | W3C | Medium — copy-paste automations |
| Screen Orientation | W3C | Medium — mobile simulation |
| Credential Management | W3C | Medium — auth flows |
| Pointer Lock API | W3C | Low |
| View Transitions API | W3C | Low |
| WebRTC | W3C | Low |
| WebTransport | W3C | Low |
| WebCodecs | W3C | Low |
| WebXR | W3C | Low |
| Web Speech API | W3C | Low |
| Ambient Light Sensor | W3C | Low |
6. Spec Compliance Issues
| Issue | Detail | File |
|---|---|---|
hidden tri-state | Spec defines "", "until-found", and absent. Only boolean implemented. | element/html/Html.zig |
Element.localName / prefix | Namespace prefix comparison not fully implemented; affects SVG/XML | element/Element.zig |
CustomElementDefinition | Needs Map<String> for observedAttributes; callback chain broken | CustomElementDefinition.zig |
URLSearchParams memory leak | TODO: "Release params on error" — leak on construction failure | URLSearchParams.zig |
| Duplicate Headers struct | Two header structs with unclear ownership; XHR vs fetch inconsistency | fetch/Headers.zig |
7. Infrastructure & Architecture
7.1 V8 Snapshot Hacks
File: src/browser/js/Snapshot.zig
V8 snapshot creation has TODO comments around function name registration. The current workaround may break with newer V8 versions. A clean function registration mechanism needs to be designed before upgrading V8.
7.2 Inspector / Debugger Support
File: src/browser/js/Inspector.zig
CDP's Debugger domain is partially wired through the V8 Inspector interface but breakpoints, step-through debugging, and heap snapshots are not functional. This limits --inspect mode for script authors debugging automation workflows.
7.3 Shadow DOM Event Retargeting
Event retargeting across shadow boundaries (spec §10.5) needs verification. composedPath() exists but correctness when events cross open/closed shadow roots is untested.
7.4 ES Module Loading
File: src/browser/js/Module.zig
Dynamic import() with complex dependency graphs and circular imports has not been validated against real-world module bundlers.
8. Test Coverage Gaps
| Area | Gap |
|---|---|
| WebGL | No tests for any WebGL methods |
| SubtleCrypto | No tests for RSA/AES operations |
| OffscreenCanvas | No rendering correctness tests |
| Animation | Only lifecycle tests; no timing accuracy |
| Custom Elements | observedAttributes callback chain untested |
| Shadow DOM | Event retargeting across boundaries not tested |
| ES Modules | Dynamic import() circular dependency not tested |
| Streams | Backpressure and byte stream modes untested |
| CDP emulation | Noop methods have no behavior tests |
| XHR arraybuffer | Response type arraybuffer not tested (unimplemented) |
| FontFaceSet | Event timing relative to parse state untested |
| CSS @supports | Parsing and cascade behavior untested |
| CORS | No tests validating CORS enforcement (or lack thereof) |
9. Prioritized Roadmap
Priority 1 — High Impact, Feasible
| Feature | Files to touch | Rationale |
|---|---|---|
| Approximate layout metrics | Element.zig + new layout/ module | Unblocks visibility checks; essential for agent interaction |
| CORS enforcement | Fetch.zig, HttpClient.zig | Security correctness; required for spec compliance |
| ImageBitmap | New ImageBitmap.zig + OffscreenCanvas.zig | Unblocks canvas pipeline |
CDP setDeviceMetricsOverride | cdp/domains/emulation.zig | Playwright device emulation is silently broken |
performance.timing real values | performance/Performance.zig | Scripts hang on load detection |
@supports CSS parsing | css/Parser.zig | Many modern stylesheets gate on feature detection |
hidden tri-state | element/html/Html.zig | Simple spec fix, affects form UX testing |
:placeholder-shown matching | selector/Selector.zig | One-line fix, styling correctness |
Priority 2 — Medium Impact
| Feature | Files to touch | Rationale |
|---|---|---|
| Web Workers (basic) | New worker/ module + V8 isolate | SPA compatibility |
| SubtleCrypto RSA/AES | SubtleCrypto.zig + crypto/ | Auth library support |
XHR arraybuffer / blob | xhr/XMLHttpRequest.zig | Binary data fetching |
| Canvas 2D rendering | canvas/CanvasRenderingContext2D.zig | Chart/captcha support |
| Broadcast Channel | New BroadcastChannel.zig | Inter-tab messaging |
| Clipboard API | New Clipboard.zig | Copy-paste automation |
CDP requestWillBeSentExtraInfo | cdp/domains/network.zig | Cookie inspection in Playwright |
getComputedStyle() | Element.zig + StyleManager.zig | Style introspection |
Animation KeyframeEffect | animation/Animation.zig + new files | Real animation timing |
Priority 3 — Lower Impact or High Complexity
| Feature | Rationale |
|---|---|
| IndexedDB | High complexity; critical for PWAs but rarely needed for agents |
| Service Workers | Requires Workers + Cache API first |
| File API | Headless design limits utility |
| WebRTC | Very high complexity, low agent use case |
| Full WebGL rendering | GPU pipeline; very high complexity |
| V8 Inspector full debugger | Developer tooling, not agent use |
| CSS @import chaining | Affects style correctness for complex sites |
| CustomElementDefinition observed attrs | Web Components completeness |
11. Appendix: TODO/FIXME Hotspots
| File | TODO count | Theme |
|---|---|---|
src/cdp/domains/emulation.zig | 4 | Device simulation |
src/cdp/domains/browser.zig | 5 | Browser info stubs |
src/browser/webapi/xhr/XMLHttpRequest.zig | 3 | Response types |
src/browser/webapi/canvas/OffscreenCanvasRenderingContext2D.zig | many noops | Rendering |
src/browser/js/Snapshot.zig | 2 | V8 naming hacks |
src/browser/css/Tokenizer.zig | 1 | URL parsing |
src/browser/webapi/AbstractRange.zig | 1 | StaticRange |
src/browser/webapi/Selection.zig | 1 | getComposedRanges |
src/browser/webapi/SubtleCrypto.zig | 1 | pkcs8 format |
src/browser/webapi/FontFaceSet.zig | 1 | Event timing |
src/browser/webapi/encoding/TextEncoderStream.zig | 1 | Encoding |
src/browser/webapi/event/InputEvent.zig | 1 | dataTransfer |
src/browser/webapi/URLSearchParams.zig | 1 | Memory leak |