Skip to content

dashn9/rustenium

Repository files navigation

Rustenium

The most robust, high-performance multi-protocol browser automation library for Rust.

Rustenium provides a powerful and ergonomic API for browser automation using the WebDriver BiDi and Chrome DevTools Protocol (CDP). It offers both low-level control and high-level abstractions for common automation tasks, with the ability to use BiDi and CDP independently or together.

Like Puppeteer / Playwright, but for RUST.

Features

  • Dual Protocol Support: Full WebDriver BiDi and Chrome DevTools Protocol (CDP) support
  • Multi-Browser: Chrome and Firefox support out of the box
  • Flexible Launch Modes: Manage the browser yourself, connect to an existing instance, or let the driver handle it
  • Optional Protocols: Enable BiDi, CDP, or both — connect and disconnect at runtime
  • Auto-Download: Automatically downloads Chrome, chromedriver, and Firefox if not present
  • Flexible Input Methods:
    • BidiMouse: Direct, precise mouse movements for fast automation
    • HumanMouse: Realistic mouse movements with Bezier curves and jitter to mimic human behavior
    • Keyboard: Full keyboard support with modifier keys
    • Touchscreen: Multi-touch gesture support for mobile testing
  • CSS & XPath Selectors: Convenient macros (css!(), xpath!()) for element location
  • Screenshot Capture: Take screenshots of elements or entire pages
  • Network Interception: Monitor and intercept network requests with BiDi
  • Event System: Subscribe to browser events in real-time
  • Script Evaluation: Execute JavaScript with preload script support
  • Timezone Emulation: Emulate different timezones for testing
  • Device Emulation: Emulate device metrics via CDP for responsive testing
  • Tab Management: Create and manage browser tabs via CDP
  • Type-Safe API: Leverages Rust's type system for compile-time safety

Installation

Add Rustenium to your Cargo.toml:

[dependencies]
rustenium = { version = "1.0.1", features = ["macros"] }
tokio = { version = "1", features = ["full"] }

Browser Support

Chrome

Chrome uses chromedriver for BiDi and connects directly for CDP. Both protocols can be used independently or together.

use rustenium::browsers::{chrome, ChromeConfig, ChromeLaunchMode};

// Default — Rustenium starts Chrome and attaches chromedriver
let mut browser = chrome(None).await;

// DriverManaged — Chromedriver spawns and manages Chrome
let config = ChromeConfig {
    launch_mode: ChromeLaunchMode::DriverManaged,
    ..Default::default()
};
let mut browser = chrome(Some(config)).await;

// Remote — Connect to an existing Chrome instance
let config = ChromeConfig {
    launch_mode: ChromeLaunchMode::Remote(9222),
    ..Default::default()
};
let mut browser = chrome(Some(config)).await;

Firefox

Firefox has built-in WebDriver BiDi support — no separate driver needed. Rustenium connects directly to Firefox's BiDi WebSocket.

use rustenium::browsers::{firefox, FirefoxConfig, FirefoxLaunchMode};

// Default — Rustenium starts Firefox and connects directly
let mut browser = firefox(None).await;

// With custom config
let config = FirefoxConfig {
    remote_debugging_port: Some(9222),
    browser_flags: Some(vec!["--headless".to_string()]),
    ..Default::default()
};
let mut browser = firefox(Some(config)).await;

// Remote — Connect to an existing Firefox instance
let config = FirefoxConfig {
    launch_mode: FirefoxLaunchMode::Remote(9222),
    ..Default::default()
};
let mut browser = firefox(Some(config)).await;

Protocol Selection (Chrome)

BiDi is enabled by default. CDP is opt-in. You can use them independently or together, though note that CDP can become buggy in the presence of an active BiDi connection. Connecting BiDi after CDP does not affect the CDP setup.

use rustenium::browsers::ChromeConfig;

// BiDi only (default)
let config = ChromeConfig::default();

// CDP only
let config = ChromeConfig {
    enable_bidi: false,
    enable_cdp: true,
    ..Default::default()
};

// Both
let config = ChromeConfig {
    enable_bidi: true,
    enable_cdp: true,
    ..Default::default()
};

You can also connect protocols at runtime:

use rustenium::browsers::{ChromeBrowser, ChromeConfig};

// Start with CDP only, then connect BiDi later
let mut browser = ChromeBrowser::new(ChromeConfig {
    enable_bidi: false,
    enable_cdp: true,
    ..Default::default()
}).await;

// ... do CDP work ...

browser.connect_bidi().await; // connects BiDi without affecting CDP

Quick Start

use rustenium::browsers::chrome;
use rustenium_macros::css;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create a Chrome browser instance
    let mut browser = chrome(None).await;

    // Navigate to a page
    browser.navigate("https://example.com").await?;

    // Find and interact with elements
    let search_box = browser.find_node(css!("input[type='search']")).await?.expect("No node found");
    search_box.screenshot().await?;

    let mut submit_button = browser.find_node(css!("button[type='submit']")).await?.expect("No node found");
    submit_button.mouse_click().await?;

    // Take a screenshot
    browser.screenshot().await?;

    // Close the browser
    browser.close().await?;

    Ok(())
}

Examples

Browser Setup and Navigation (BiDi)

use rustenium::browsers::{chrome, ChromeConfig, NavigateOptionsBuilder};
use rustenium_bidi_definitions::browsing_context::types::ReadinessState;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut config = ChromeConfig::default();

    config.capabilities.add_arg("--disable-gpu")
        .add_args(["--window-size=1920,1080"])
        .accept_insecure_certs(true);

    let mut browser = chrome(Some(config)).await;

    // Navigate and wait for load
    browser.navigate_with_options("https://example.com",
        NavigateOptionsBuilder::default()
            .wait(ReadinessState::Interactive)
            .build()
    ).await?;

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

CDP Navigation and Tab Management

use rustenium::browsers::{ChromeConfig, ChromeBrowser};
use rustenium::browsers::cdp_browser::CdpBrowser;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut browser = ChromeBrowser::new(ChromeConfig {
        enable_bidi: false,
        enable_cdp: true,
        ..Default::default()
    }).await;

    // Navigate via CDP
    CdpBrowser::navigate(&mut browser, "https://example.com").await?;

    // Create a new tab
    CdpBrowser::create_tab(&mut browser, "https://example.org").await?;

    // Emulate device metrics
    CdpBrowser::emulate_device_metrics(&mut browser, 375, 812, 3.0, true).await?;

    Ok(())
}

Finding Elements

use rustenium::browsers::chrome;
use rustenium_macros::{css, xpath};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut browser = chrome(None).await;
    browser.navigate("https://example.com").await?;

    // Using CSS selectors
    let element = browser.find_node(css!("#my-id")).await?;
    let buttons = browser.find_nodes(css!(".btn-primary")).await?;

    // Using XPath
    let headers = browser.find_nodes(xpath!("//h1[@class='title']")).await?;

    // Wait for elements to appear
    let node = browser.wait_for_node(css!(".dynamic-content")).await?;

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

Mouse Input — Precise Movements

use rustenium::browsers::chrome;
use rustenium::input::{MouseMoveOptions, MouseClickOptions, Point};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut browser = chrome(None).await;
    let context_id = browser.get_active_context_id()?;

    // Instant, precise movements
    browser.mouse().move_to(Point { x: 100.0, y: 200.0 }, &context_id, MouseMoveOptions {
        steps: Some(5),
        ..Default::default()
    }).await?;

    browser.mouse().click(None, &context_id, MouseClickOptions::default()).await?;

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

Mouse Input — Human-Like Movements

use rustenium::browsers::chrome;
use rustenium::input::{MouseMoveOptions, MouseClickOptions, Point};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut browser = chrome(None).await;
    let context_id = browser.get_active_context_id()?;

    // Realistic movements with Bezier curves and jitter
    browser.human_mouse().move_to(Point { x: 100.0, y: 200.0 }, &context_id, MouseMoveOptions::default()).await?;
    browser.human_mouse().click(None, &context_id, MouseClickOptions::default()).await?;

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

Keyboard Input

use rustenium::browsers::chrome;
use rustenium::input::KeyboardTypeOptions;
use rustenium_macros::css;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut browser = chrome(None).await;
    let context_id = browser.get_active_context_id()?;

    browser.navigate("https://example.com").await?;

    let text = browser.wait_for_node(css!("#text")).await?.expect("No node exists");

    // Type text with delay between keystrokes
    browser.keyboard().type_text(
        &text.get_inner_text().await,
        &context_id,
        Some(KeyboardTypeOptions { delay: Some(36) }),
    ).await?;

    // Modifier key combinations (Ctrl+A)
    browser.keyboard().down("Control", &context_id).await?;
    browser.keyboard().press("a", &context_id, None).await?;
    browser.keyboard().up("Control", &context_id).await?;

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

Network Interception

use rustenium::browsers::chrome;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut browser = chrome(None).await;

    // Intercept and handle network requests
    browser.on_request(|request| async move {
        println!("Request URL: {}", request.url());

        if request.url().contains("ads.example.com") {
            let _ = request.abort().await;
            return;
        }
        request.continue_().await;
    }).await?;

    // Add authentication handler
    browser.authenticate("username", "password").await?;

    browser.navigate("https://example.com").await?;

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

Script Evaluation & Preload Scripts

use rustenium::browsers::chrome;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut browser = chrome(None).await;

    // Evaluate JavaScript
    let result = browser.evaluate_script(
        "document.title".to_string(),
        true,
    ).await?;

    // Add a preload script that runs on every page load
    let script_id = browser.add_preload_script(
        "() => { window.__injected = true; }".to_string(),
    ).await?;

    // Remove it later
    browser.remove_preload_script(script_id).await?;

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

Timezone Emulation

use rustenium::browsers::chrome;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut browser = chrome(None).await;

    browser.emulate_timezone(Some("Asia/Tokyo".to_string())).await?;
    browser.navigate("https://example.com").await?;

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

Firefox Example

use rustenium::browsers::firefox;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut browser = firefox(None).await;

    browser.navigate("https://example.com").await?;

    // All BiDi features work the same as Chrome
    let nodes = browser.find_nodes(css!("h1")).await?;
    browser.screenshot().await?;

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

Crate Structure

Crate Description
rustenium Main library — browser impls, input devices, node interactions
rustenium-core Protocol transport, sessions, connections, event system
rustenium-bidi-definitions WebDriver BiDi protocol type definitions
rustenium-cdp-definitions Chrome DevTools Protocol type definitions
rustenium-macros Procedural macros (css!, xpath!)
rustenium-generator Code generator for protocol definitions from specs

Browser Support

Browser BiDi CDP Auto-Download
Chrome/Chromium Yes Yes Yes
Firefox Yes No Yes

Requirements

  • Rust 1.85 or later (2024 edition)
  • Chrome or Firefox (auto-downloaded if not present)

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgements

  • HumanCursor — the human-like mouse trajectory algorithm (Bezier curves, easing, Gaussian distortion) is ported from this excellent Python library
  • Chromium Oxide — inspiration for the protocol definition code generator
  • The Ish Bot — some bot automation inspiration was borrowed from this project

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

About

Rust browser automation over the WebDriver BiDi Protocol and CDP

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages