Skip to main content

Browser

Struct Browser 

Source
pub struct Browser { /* private fields */ }
Expand description

A running Chrome instance under zendriver control.

Browser is Clone (cheap — wraps an Arc) and Send + Sync, so the same handle can be passed across tokio::spawn boundaries. Dropping the last clone shuts down the transport and terminates Chrome via kill_on_drop (for a graceful SIGTERM-then-SIGKILL teardown call Browser::close explicitly).

Build one via Browser::builder.

Implementations§

Source§

impl Browser

Source

pub fn builder() -> BrowserBuilder

Construct a fresh BrowserBuilder (with stealth on by default).

Equivalent to BrowserBuilder::new.

§Examples
let browser = zendriver::Browser::builder().launch().await?;
Source

pub async fn connect( endpoint: impl Into<String>, ) -> Result<Browser, ZendriverError>

Attach to an already-running Chrome debug session instead of spawning.

Shortcut for Browser::builder().connect(endpoint) — see that method for endpoint formats (ws://… / http://host:port), the non-owning lifecycle (close / drop never kill the attached process), and which builder fields are ignored on the connect path.

§Errors

Same as BrowserBuilder::connect.

§Examples
let browser = zendriver::Browser::connect("http://127.0.0.1:9222").await?;
Source

pub fn main_tab(&self) -> Tab

Handle for the tab that exists when Chrome launches.

The main tab is registered eagerly at launch time and is the same Tab across every clone of this Browser.

§Examples
let tab = browser.main_tab();
tab.goto("https://example.com").await?;
Source

pub fn cdp(&self) -> &Connection

Raw root Connection for browser-scope CDP commands.

Escape hatch for advanced users who need to send CDP commands at browser scope (no sessionId) that the high-level API doesn’t wrap.

§Examples
let conn = browser.cdp();
let info = conn.call_raw("SystemInfo.getInfo", serde_json::json!({}), None).await?;
println!("{info}");
Source

pub fn cookies(&self) -> CookieJar

Browser-wide cookie store handle. Cookies in Chrome are stored once per profile and shared across all tabs, so the jar binds to the browser’s root Connection and dispatches Network.*Cookies* commands at browser scope (no sessionId).

Cheap to call — crate::CookieJar is an Arc-backed handle, and each invocation constructs a fresh wrapper around the cloned connection.

Source

pub async fn set_download_path( &self, dir: impl Into<PathBuf>, ) -> Result<(), ZendriverError>

Route downloads into dir at runtime, browser-wide, keeping each file’s server-suggested name.

Dispatches Browser.setDownloadBehavior { behavior: "allow", downloadPath: <dir> } on the root Connection (browser scope, no sessionId), so the policy applies to the current tab and any future tabs. dir must already exist.

This is the runtime counterpart to BrowserBuilder::downloads_dir (which sets the same behavior at launch). It is distinct from the expect_download capture flow (Tab::expect_download), which uses allowAndName against a private tempdir to await + save a single download; set_download_path simply lets downloads land in a known directory with their natural names.

§Errors

Returns ZendriverError::Transport / Cdp if the CDP call fails.

§Examples
browser.set_download_path("/tmp/downloads").await?;
Source

pub async fn create_browser_context_with( &self, proxy_server: Option<&str>, proxy_bypass_list: Option<&str>, ) -> Result<BrowserContext, ZendriverError>

Create a new isolated crate::BrowserContext bound to an optional proxy.

Wraps the CDP Target.createBrowserContext command: when proxy_server is Some, the returned context routes all network traffic through that upstream (mirroring Chrome’s --proxy-server flag — e.g. "http://host:port" or "http://user:pass@host:port"). When proxy_bypass_list is Some, hosts matching the pattern (e.g. "<-loopback>" or "*.internal.example.com") bypass the proxy. Either field is omitted from the params when None, not sent as null — some CDP versions reject unknown null fields.

Drop the returned handle to schedule asynchronous disposal via Target.disposeBrowserContext.

§Errors

Returns ZendriverError::Navigation if the CDP response is missing the browserContextId field; bubbles up any transport-level error from the underlying call_raw.

Source

pub async fn create_browser_context( &self, ) -> Result<BrowserContext, ZendriverError>

Create a new default crate::BrowserContext (no proxy, no bypass).

Convenience wrapper over Browser::create_browser_context_with called with both arguments as None.

§Errors

Same as Browser::create_browser_context_with.

Source

pub async fn new_tab(&self) -> Result<Tab, ZendriverError>

Open a new tab navigated to about:blank.

Returns once an internal tab registrar has registered the new Tab in the browser’s tab registry — typically within a few milliseconds of Target.createTarget’s response.

Internally:

  1. Sends Target.createTarget { url: "about:blank" } at browser scope (no session_id) — the response includes the new targetId.
  2. Polls the internal tabs registry every 50ms for up to 5s, looking for a Tab whose Tab::target_id matches. The tab registrar populates that entry asynchronously when the Target.attachedToTarget event arrives.
  3. Returns the matching Tab on success.
§Errors

Returns ZendriverError::TabNotFound if the registrar fails to register the new tab within the 5s window — usually a sign that auto-attach is misconfigured or the registrar observer crashed.

§Examples
let browser = zendriver::Browser::builder().launch().await?;
let tab = browser.new_tab().await?;
tab.goto("https://example.com").await?;
Source

pub async fn new_tab_at( &self, url: impl Into<String>, ) -> Result<Tab, ZendriverError>

Open a new tab navigated to url.

Behaves identically to Browser::new_tab but with a custom initial URL passed to Target.createTarget. The returned Tab handle is ready as soon as the internal tab registrar observer registers it; callers can issue .wait_for_load() if they need to block on the navigation.

§Examples
let tab = browser.new_tab_at("https://example.com").await?;
tab.wait_for_load().await?;
Source

pub async fn new_window(&self) -> Result<Tab, ZendriverError>

Open a new top-level OS window navigated to about:blank.

Like Browser::new_tab but passes newWindow: true to Target.createTarget, so Chrome opens a separate browser window rather than a tab in the existing one. The returned Tab is registered via the same observer path as new_tab. Mirrors nodriver’s get(new_window=True).

§Errors

Same as Browser::new_tab: ZendriverError::TabNotFound if the registrar fails to register the new window’s tab within the wait window.

§Examples
let browser = zendriver::Browser::builder().launch().await?;
let win = browser.new_window().await?;
win.goto("https://example.com").await?;
Source

pub async fn new_window_at( &self, url: impl Into<String>, ) -> Result<Tab, ZendriverError>

Open a new top-level OS window navigated to url.

Browser::new_window with a custom initial URL. Issue .wait_for_load() on the returned Tab to block on the navigation.

§Examples
let win = browser.new_window_at("https://example.com").await?;
win.wait_for_load().await?;
Source

pub async fn tabs(&self) -> Vec<Tab>

Snapshot of all currently-registered tabs.

Order is unspecified (the registry is a HashMap keyed by sessionId). Includes the main tab plus any tabs opened via Browser::new_tab or by page scripts (e.g. window.open) that auto-attach has wired into the registrar.

§Examples
for tab in browser.tabs().await {
    println!("tab {}: {}", tab.target_id(), tab.url().await?);
}
Source

pub async fn tab_count(&self) -> usize

Count of currently-registered tabs.

Equivalent to self.tabs().await.len() but avoids the Vec allocation.

§Examples
assert_eq!(browser.tab_count().await, 1);
Source

pub async fn reconnect(&self) -> Result<(), ZendriverError>

Re-establish a dropped connection to the same still-running Chrome process (scoped reconnect — see the invalidation caveat below).

When a CDP call returns ZendriverError::Disconnected the WebSocket died but the Chrome process is typically still alive — its browser-level /devtools/browser/<id> endpoint survives. reconnect re-dials that endpoint on the existing Connection (so raw event subscribers stay attached to the same broadcast bus), re-applies the WebSocket size config, re-arms Target.setAutoAttach { flatten: true } — which re-fires the observer chain so the stealth patches re-inject on every target — and refreshes the tab registry.

§Handle invalidation — IMPORTANT

Re-attaching to Chrome’s targets yields new sessionIds. Every Tab, Frame, and Element handle obtained before the reconnect caches a now-stale session id and must not be reused — calls on them will fail. After reconnect returns, re-acquire handles:

  • live page handles via Browser::tabs (the registry is rebuilt from the re-attached targets);
  • not via Browser::main_tab, which still returns the pre-reconnect handle (its cached session id is stale). Swapping the cached main-tab handle in place is part of the deferred transparent-reconnect work; for now treat main_tab() as invalid after a reconnect and use tabs().
§What is NOT restored (deferred)

This is the scoped v1. It does not replay per-feature domain arming (Network.enable, active Fetch interception rules, DOMStorage.enable, download behavior, …) that individual features turned on before the drop — re-arm those yourself after reconnecting. Transparent handle-preserving reconnect (session-id remap so existing Tabs keep working) is tracked as follow-up work.

§Errors

Returns ZendriverError::Disconnected if this browser was constructed without a known ws endpoint (e.g. a test harness) so there is nothing to re-dial, and ZendriverError::Transport / ZendriverError::Cdp if the re-dial or the post-reconnect handshake fails. On a failed re-dial the existing (dead) connection is left untouched.

§Examples
// A long-running scraper that survives a dropped socket:
let tab = browser.main_tab();
if let Err(zendriver::ZendriverError::Disconnected) = do_work(&tab).await {
    browser.reconnect().await?; // re-dial the same Chrome process
    // Old handles are stale now — re-acquire from the rebuilt registry:
    let tab = browser.tabs().await.into_iter().next().unwrap();
    do_work(&tab).await?;
}
Source

pub async fn close(self) -> Result<(), ZendriverError>

Graceful shutdown of the Chrome subprocess.

Cancels the transport, sends SIGTERM to Chrome, waits up to 5s, then SIGKILLs on timeout. Cleans up the user_data_dir tempdir if one was allocated at launch time.

For a browser produced by BrowserBuilder::connect (we did not spawn Chrome) this shuts down only the transport and leaves the attached process running.

§Examples
let browser = zendriver::Browser::builder().launch().await?;
// ... drive the browser ...
browser.close().await?;
Source

pub async fn grant_permissions( &self, perms: &[PermissionType], origin: Option<&str>, ) -> Result<(), ZendriverError>

Grant perms to origin (or browser-wide when origin is None).

Wraps the CDP Browser.grantPermissions command, sent at browser scope (no sessionId). Each PermissionType is mapped to its camelCase wire string. When origin is Some, the grant is scoped to that origin (e.g. "https://example.com"); when None, the origin key is omitted so the grant applies browser-wide.

Granting a permission pre-authorizes it without the usual user prompt — useful for unattended automation that would otherwise stall on a permission dialog (geolocation, clipboard, notifications, …).

§Errors

Bubbles up any transport-level error from the underlying call_raw (e.g. the connection was shut down).

§Examples
browser
    .grant_permissions(
        &[PermissionType::Geolocation, PermissionType::Notifications],
        Some("https://example.com"),
    )
    .await?;
Source

pub async fn grant_all_permissions(&self) -> Result<(), ZendriverError>

Grant every PermissionType browser-wide.

Convenience wrapper over Browser::grant_permissions called with PermissionType::ALL and no origin — mirrors nodriver / zendriver-py’s grant_all_permissions. Pre-authorizes the full CDP permission set so automated runs never stall on a permission prompt.

§Errors

Same as Browser::grant_permissions.

§Examples
browser.grant_all_permissions().await?;
Source

pub async fn reset_permissions(&self) -> Result<(), ZendriverError>

Reset all previously-granted permissions to their default prompt state.

Wraps the CDP Browser.resetPermissions command, sent at browser scope (no sessionId). Clears every override installed via Browser::grant_permissions / Browser::grant_all_permissions.

§Errors

Bubbles up any transport-level error from the underlying call_raw.

§Examples
browser.reset_permissions().await?;

Trait Implementations§

Source§

impl Clone for Browser

Source§

fn clone(&self) -> Browser

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Browser

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more