Browser Commander
A Rust library for universal browser automation that provides a unified API for different browser automation engines. The key focus is on stoppable page triggers - ensuring automation logic is properly mounted/unmounted during page navigation.
Installation
Add this to your Cargo.toml:
[]
= "0.9"
= { = "1.0", = ["full"] }
Core Concept: Page State Machine
Browser Commander manages the browser as a state machine with two states:
+------------------+ +------------------+
| | navigation start | |
| WORKING STATE | -------------------> | LOADING STATE |
| (action runs) | | (wait only) |
| | <----------------- | |
+------------------+ page ready +------------------+
LOADING STATE: Page is loading. Only waiting/tracking operations are allowed. No automation logic runs.
WORKING STATE: Page is fully loaded (30 seconds of network idle). Page triggers can safely interact with DOM.
Quick Start
use *;
async
Features
- Unified API across multiple browser engines
- Native Rust Chromiumoxide support
- Playwright and Puppeteer support through a Node.js bridge
- Built-in navigation safety handling
- Element visibility and scroll management
- Click, fill, and other interaction support with verification
- Async/await support with Tokio
API Reference
Browser Launch
use *;
// Launch with chromiumoxide (CDP-based)
let options = chromiumoxide
.headless
.user_data_dir;
let result = launch_browser.await?;
Playwright and Puppeteer
Rust does not have official Playwright or Puppeteer bindings. To keep the same engine names available from Rust, Browser Commander starts a local Node.js bridge and delegates operations to the official Node packages.
Install the package you want Node to resolve:
Then configure the bridge working directory if the packages are not installed from the process current directory:
use *;
let playwright = playwright
.headless
.node_working_dir;
let puppeteer = puppeteer
.headless
.node_working_dir;
You can also set a custom Node executable:
let options = playwright
.node_executable
.node_working_dir;
LaunchOptions::fantoccini() is still accepted for source compatibility, but launch_browser() does not yet start a managed WebDriver process.
Navigation
// Navigate to URL
goto.await?;
// Navigate with options
let nav_options = NavigationOptions ;
goto.await?;
// Wait for URL to match condition
wait_for_url_condition.await?;
Element Interactions
// Click a button
click_button.await?;
// Click with options
let click_options = ClickOptions ;
click_button.await?;
// Fill text area
fill_text_area.await?;
// Scroll element into view
scroll_into_view.await?;
// Keyboard interactions
press_key.await?;
press_key.await?;
type_text.await?;
key_down.await?;
key_up.await?;
Element Queries
// Check visibility
let visible = is_visible.await?;
// Check if enabled
let enabled = is_enabled.await?;
// Get text content
let text = text_content.await?;
// Get attribute value
let href = get_attribute.await?;
// Count matching elements
let count = count.await?;
Utilities
// Wait for a duration
wait.await;
// Get current URL
let url = get_url.await?;
// Parse URL
let parsed = parse_url?;
// Evaluate JavaScript
let result: String = evaluate.await?;
Modules
core- Core types and traits (constants, engine adapter, logger)elements- Element operations (selectors, visibility, content)interactions- User interactions (click, scroll, fill, keyboard)browser- Browser management (launcher, navigation)utilities- General utilities (URL handling, wait operations)high_level- High-level DRY utilities
Prelude
For convenience, import everything commonly needed with:
use *;
Extensibility / Escape Hatch
browser-commander cannot anticipate every browser API. When you need an API that is not yet supported, you can access the raw underlying engine objects directly as an official extensibility escape hatch.
Using LaunchResult raw fields
launch_browser() returns a LaunchResult with a browser field that contains the engine type and configuration. When using the underlying engine crate (e.g. chromiumoxide) directly, the raw browser and page objects are accessible from the engine crate:
use *;
let options = chromiumoxide.headless;
let result = launch_browser.await?;
// Access engine metadata
println!;
println!;
// For engine-specific APIs not yet in browser-commander,
// use the underlying engine crate directly alongside browser-commander.
// For example, with chromiumoxide:
// let (browser, mut handler) = Browser::launch(BrowserConfig::builder()...).await?;
// let page = browser.new_page("about:blank").await?;
// // Use page.pdf(), page.emulate_media(), page.keyboard() etc.
Why This Matters
- Users can adopt browser-commander incrementally while retaining access to full engine APIs
- No need for fragile
_pageprivate-field hacks - Missing APIs can be reported as issues while users remain unblocked