Why Gecko Is the Hard One
July 12, 2026 · Ryan X. Charles
Astrohacker Terminal runs real browser engines in the same window as your shell: Chromium, WebKit, Ladybird today, and Gecko on the long path. Same protocol. Same “open a page beside the terminal” idea. Very different amount of pain.
This is not a dunk on Firefox. Gecko is excellent software. It is also the engine whose desktop embedding story was never written for people like us. We learned that the expensive way—research, builds, experiments, and a few honest dead ends.
What “supported” means here
We are not shipping a Firefox skin. We need the same product shape the other engines already have:
- A page loads in a terminal pane, not a random window somewhere on the desktop.
- Geometry comes from the terminal (resize the pane → the web surface follows).
- Input and focus go into that page.
- Launch is named and boring: pick the engine, get
BrowserReady, keep going.
Protocol scaffolding is necessary. It is not sufficient. A helper that speaks the right messages while the only visible UI is a floating Firefox chrome window is progress, not parity.
Four engines, four host stories
- WebKit — You host a real
WKWebView(NSView). Closest to “put a view in a window.” - Chromium — Content API / known fork patterns. Heavy, but designed to be hosted.
- Ladybird — LibWeb + their shell. Young; hard work, but your architecture.
- Gecko — No official desktop embed equivalent. You reverse-engineer Firefox.
WebKit wins the “easy” prize for a reason: the modern API is a view. Input often behaves like any other AppKit first responder. Compositing is “the view is there.”
Chromium is a different kind of hard: giant tree, multi-process, custom compositor bridges (on our stack, the CAContext / host-layer story). But Chromium still has a first-class content hosting culture. You are not pretending to be Chrome’s UI; you are attaching to an embedding surface people expect to fork and ship.
Ladybird is hard because the engine is still growing—not because Mozilla (or anyone) removed the front door.
Gecko is hard because the front door moved.
The missing front door
For years people said “embed Gecko with libxul / XPCOM.” That was a real story once. Product reality now is different:
- The modern, supported embed product is GeckoView — and it is Android-only.
- Desktop Firefox is optimized to be Firefox, not a content widget you drop into someone else’s process.
- Out-of-tree “call the old embedding exports and link” is a nostalgia path. We tried it. It failed as a practical route for our binary.
So the work becomes: live in the Firefox tree, boot enough of XRE /
XPCOM, and find whatever internal surfaces still create a browser context
(nsIWindowlessBrowser and friends), all without a vendor promise that those
hooks stay stable for third parties.
That is a research program with a build farm attached—not a weekend wrapper.
Compositing is a second cliff
Even after you can load a URL, you still have to show it in a pane.
Chromium’s macOS story in our stack leans on a compositor path we already understood: cross-process layers, hostable surface IDs, a known dance into the terminal.
Gecko’s modern path is WebRender → IOSurface-backed native layers, with cross-process handoff that is not “the same as Chromium’s CAContext trick.” Headless mode makes this worse: Firefox’s headless path is happy to disable GPU compositing, which is exactly what you do not want if the goal is a live pane, not a software screenshot.
Preferred long-term shape (from our architecture notes): something like an NSView overlay or a shared surface drawn into a Ghostboard-owned view. Neither is free. Both require owning the host surface, not just moving a foreign window around with accessibility APIs.
What we already proved
We did not only read source. We built Nightly, shipped an ah-geckod helper,
spoke the TermSurf protocol, and ran real loads.
Rough scoreboard:
- Full Firefox build from a monorepo checkout — works (expensive, real).
- Process-windowless load with TabReady / URL / title callbacks — works (in-tree).
- Ghostboard launch with named
gecko— works. - Visible window + geometry follow (process chrome aligned to the pane via platform geometry) — works as a milestone.
- Out-of-tree classic embedding init — fail.
- True pane-hosted webview + real input/focus like the other engines — not done yet. That is the open product work.
The milestone matters. It proves Gecko can live in our multi-engine world at all. It also tempts a bad conclusion: “the window tracks the pane, ship it.” That is how you quietly redefine “supported.”
A pane-aligned Firefox window is not a pane-hosted webview. Clip, z-order, focus, and input all tell on you.
Protocol complete ≠ product complete
Our protocol rule for engines is strict: every relevant message is either implemented or explicitly unsupported—no silent ignores, no hangs.
Gecko can satisfy that rule while mouse and key still log “until embed.” That is honest scaffolding. It is also a neon sign: the hard part is still the host surface and input path, not another protobuf row.
If an engine only loads pages in a sidecar window and cannot take TermSurf input, it is not “the same as WebKit in the terminal.” It is a demo of intent.
Why we still want it
Strategic reason, not nostalgia: a multi-engine terminal that only runs Chromium-family and WebKit is already powerful. Adding Gecko means the same workbench can exercise the last major production browser lineage on the open web—same session habits, different engine bugs, different layout, different reality check.
Ladybird is the future-facing independent engine. Gecko is the production Firefox lineage. Both belong in the set if the claim is “major engines,” not “engines that were convenient to wrap.”
What “done” has to mean next
We opened a follow-on bar that forbids the soft landing:
- Content is hosted in or composited into the terminal pane surface.
- Geometry is driven by TermSurf / the board—not only by chasing a foreign top-level window.
- Input and focus affect the page.
- Launch still looks like the other engines from the user’s point of view.
Process chrome can stay a temporary ladder rung. It cannot be the definition of done.
Closing
Gecko is the hard one because embedding was never the product on desktop, the compositor contract is foreign to the Chromium path we already ship, and honest intermediate wins (windowless load, named launch, a visible window that follows the pane) look deceptively like finish lines.
WebKit gives you a view. Chromium gives you a hosting culture. Ladybird gives you a tree you can still shape. Gecko gives you Firefox—and then the work of making Firefox behave like a webview inside someone else’s window.
We are doing that work on purpose. The easy engines make the product real. The hard engine makes the product complete.