ferridriver 0.4.0

Browser automation in Rust with a Playwright-compatible API. Four pluggable backends: CDP pipe, CDP WebSocket, Playwright WebKit, Firefox BiDi.
Documentation

ferridriver

crates.io docs.rs MSRV License

Browser automation written in Rust with a Playwright-compatible API. Four backends behind one surface — Browser, BrowserContext, Page, Frame, Locator, ElementHandle — selected per launch.

Backend kind Browser Transport
BackendKind::CdpPipe (default) Chromium / Chrome CDP over Unix pipes (fd 3/4)
BackendKind::CdpRaw Chromium / Chrome CDP over WebSocket
BackendKind::WebKit Playwright WebKit Playwright Inspector protocol over pw_run.sh
BackendKind::Bidi Firefox WebDriver BiDi over WebSocket

This crate is the Rust core. For the test runner, BDD framework, MCP server, CLI binary, or Node binding, depend on ferridriver-test, ferridriver-bdd, ferridriver-mcp, ferridriver-cli, or @ferridriver/node respectively.

Usage

use ferridriver::browser_type::chromium;
use ferridriver::options::LaunchOptions;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let browser = chromium().launch(LaunchOptions::default()).await?;
    let page = browser.page().await?;

    page.goto("https://example.com", None).await?;
    let title = page.title().await?;
    println!("{title}");

    page.locator("#search").fill("rust").await?;
    page.locator("button[type=submit]").click().await?;
    page.wait_for_load_state(Some("networkidle")).await?;

    browser.close().await?;
    Ok(())
}

firefox() and webkit() are factories with the same shape. BrowserType::with_backend(...) and chromium_with(BrowserTypeOptions { ... }) let you pin a backend explicitly.

Public API surface

  • BrowserType::launch, BrowserType::connect, BrowserType::connect_over_cdp, BrowserType::launch_persistent_context
  • Browser::new_context, Browser::default_context, Browser::new_page, Browser::page, Browser::close
  • BrowserContext::new_page, pages, cookies, add_cookies, clear_cookies, grant_permissions, set_geolocation, add_init_script, route, set_extra_http_headers, storage_state, close
  • Page::goto, go_back, go_forward, reload, url, title, content, markdown, evaluate, evaluate_handle, screenshot, pdf, route, wait_for_url, wait_for_load_state, wait_for_navigation, wait_for_event, wait_for_request, wait_for_response, wait_for_dialog, wait_for_file_chooser, wait_for_download, add_init_script, expose_function, set_extra_http_headers, emulate_media, set_viewport_size, start_tracing, stop_tracing, keyboard(), mouse(), touchscreen(), plus the shorthand action and query methods (click, fill, is_visible, text_content, …).
  • Locator: lazy, strict-by-default. click, fill, hover, tap, drag_to, select_option, check, uncheck, set_input_files, screenshot, evaluate, evaluate_all, count, all, first, last, nth, and, or, filter, locator, frame_locator, get_by_role, get_by_text, get_by_label, get_by_placeholder, get_by_alt_text, get_by_title, get_by_test_id, wait_for.
  • Frame, FrameLocator, ElementHandle, Route, Request, Response, Dialog, Download, FileChooser, Video, JSHandle.

Errors

FerriError is the single public error type. Variants the user is most likely to match on:

Variant Helper Notes
Timeout is_timeout_error() Auto-wait or expect timeout. name() returns "TimeoutError" for JS interop.
TargetClosed is_target_closed_error() Page or browser closed mid-operation.
StrictModeViolation is_strict_mode_violation() Locator resolved to >1 element.
Unsupported is_unsupported() Backend cannot perform the operation (e.g. HAR on BiDi).
InvalidSelector Selector failed to parse.
Navigation / Protocol / Backend / Evaluation / Snapshot / Io / Json Underlying failure surfaces.

Selectors

Full Playwright selector engine — CSS (default), role=, text=, label=, placeholder=, alt=, title=, testid=, xpath=, id=, nth=, visible=true, has=, has-text=, has-not=, has-not-text=, chained with >>. The engine compiles in Rust and is injected once per page; no per-query JS eval.

Feature parity by backend

All four backends speak the same Browser / Page / Locator surface. Genuinely-unsupported operations return FerriError::Unsupported rather than placeholder values. Today's gaps:

  • HAR recording: BiDi returns Unsupported.
  • Download interception: BiDi returns Unsupported.
  • CDP-only tracing: WebKit and BiDi.

For everything else, the same test runs on every backend without change.

License

MIT OR Apache-2.0