# Servo Embedding Architecture ## Decision ELY is a Servo-based browser. The page renderer lives in the application process and follows Servo's embedder model: ```text ┌──────────────────────────── ELY App Process ────────────────────────────┐ │ │ │ GPUI chrome │ │ ┌────────────────────────────────────────────────────────────────────┐ │ │ │ Sidebar Toolbar Tabs Settings │ │ │ └────────────────────────────────────────────────────────────────────┘ │ │ │ │ Servo content host │ │ ┌────────────────────────────────────────────────────────────────────┐ │ │ │ Servo + WebView + RenderingContext │ │ │ │ notify_new_frame_ready -> repaint -> paint -> present │ │ │ └────────────────────────────────────────────────────────────────────┘ │ │ │ └──────────────────────────────────────────────────────────────────────────┘ ``` The app process owns WebView lifecycle, navigation, input, permissions, frame readiness, and rendering context presentation. ## Current Code Boundary ```text WebSurfaceStore -> GPUI NativeSurface -> LiveRuntimeWorker -> ServoLiveClient -> SoftwareServoHost -> servo::Servo + servo::WebView + servo::WindowRenderingContext ``` `ServoLiveClient` is now an in-process adapter over `ely_servo_host::SoftwareServoHost`. It shares one Servo runtime across profile scopes, creates multiple WebViews inside that runtime, and routes scroll, hover, click, keyboard, resize, zoom, navigation, and permissions through the `ServoHost` API. Normal page display now enters GPUI through `native_surface(...)`. GPUI creates a platform child surface for the content bounds, passes that raw handle into Servo's `WindowRenderingContext`, and presents with `paint_without_readback_with_completion`. The RGBA readback path remains inside `ely_servo_host` for low-level tests. Current platform child surfaces: - macOS: child `NSView` with an AppKit raw window handle. - Windows: child `HWND` with a Win32 raw window handle. - Linux/X11: child XCB window with XCB display/window handles. - Linux/Wayland: child `wl_surface` attached as a `wl_subsurface` with Wayland display/surface handles. ## Upstream Servo Route ELY pins Servo upstream commit `bc469fd5c17137373458508f88c3907cd1fcb69a` (workspace version `0.4.0`) in `Cargo.toml` and `Cargo.lock`. That revision includes the July 2026 security fixes that landed after the `v0.3.0` regular release. Servo's LTS line uses a separate six-month cadence. Servo's own shell route is the model for the final ELY rendering path: ```text Window event -> Servo spin_event_loop -> WebViewDelegate::notify_new_frame_ready -> window request_redraw -> WebView::paint -> RenderingContext::present ``` Relevant upstream evidence from Servo `bc469fd5c17137373458508f88c3907cd1fcb69a`: - `ports/servoshell/window.rs` creates `WebViewBuilder::new(state.servo(), platform_window.rendering_context())`. - `ports/servoshell/window.rs` repaints with `webview.paint()` and `rendering_context().present()`. - `ports/servoshell/running_app_state.rs` handles `notify_new_frame_ready` by marking the owning window for repaint. - `components/paint/paint.rs` owns one WebRender painter per `RenderingContext` and keeps paint coordination inside Servo's rendering pipeline. ## Target Boundaries ```text crates/ely_browser_core Owns browser domain state: tabs, spaces, profiles, search, settings. crates/ely_app/src/shell Owns GPUI chrome and command surfaces. crates/ely_app/src/servo_embed Owns in-process Servo runtime, platform content view attachment, WebView lifecycle, repaint dispatch, and web input routing. crates/ely_servo_host Owns the current in-process compatibility adapter while the native platform rendering surface lands. ``` Each production source file in the final embedding path stays below 500 lines. Large responsibilities split by ownership: - `runtime.rs`: `Servo`, wake handling, webview registry. - `platform_view.rs`: platform content surface attachment. - `delegate.rs`: Servo `WebViewDelegate` implementation. - `input.rs`: GPUI event to Servo input conversion. - `paint.rs`: repaint and present coordination. - `metadata.rs`: title, URL, favicon, load-state propagation. ## Migration Slices 1. App process owns the Servo runtime and WebView registry. 2. App process routes web input and navigation directly into Servo. 3. GPUI exposes one `NativeSurfaceHandle` element contract for platform child surfaces. 4. macOS creates a child `NSView` and hands the AppKit raw handle to Servo. 5. Windows creates a child `HWND` and hands the Win32 raw handle to Servo. 6. Linux/X11 creates a child XCB window and hands the XCB raw handle to Servo. 7. Linux/Wayland creates a child `wl_surface`/subsurface and hands the Wayland raw handle to Servo. 8. GPUI web page display uses native content surface presentation for every platform target. ## Acceptance Gates - `cargo run` opens a normal web page with Servo inside the ELY app process. - Scrolling a live web page uses Servo input events and Servo repaint callbacks. - Page display presents through a Servo rendering context. - macOS, Windows, Linux/X11, and Linux/Wayland each have a platform child surface implementation under the same `NativeSurfaceHandle` contract. - Address/search, tabs, spaces, profiles, settings, permissions, and sync compile through existing domain APIs. - Every new source file stays below 500 lines. - User-facing chrome stays clean. ## First Platform Surface Target ```text GPUI Window -> GPUI NativeSurface element -> platform child surface for web content bounds -> Servo WindowRenderingContext -> Servo WebView ``` This slice gives Servo a native surface in the ELY app process. The compatibility adapter remains a narrow bridge for tests and non-normal display probes.