Files
ELY-Browser/docs/servo-embedding-architecture.md
T
2026-05-18 13:58:36 -04:00

6.6 KiB

Servo Embedding Architecture

Decision

ELY is a Servo-based browser. The page renderer lives in the application process and follows Servo's embedder model:

┌──────────────────────────── 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

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

Servo's own shell route is the model for the final ELY rendering path:

Window event
  -> Servo spin_event_loop
  -> WebViewDelegate::notify_new_frame_ready
  -> window request_redraw
  -> WebView::paint
  -> RenderingContext::present

Relevant upstream evidence from Servo 7c48af7:

  • 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

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

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.