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
impl Browser
Sourcepub fn builder() -> BrowserBuilder
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?;Sourcepub async fn connect(
endpoint: impl Into<String>,
) -> Result<Browser, ZendriverError>
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?;Sourcepub fn cdp(&self) -> &Connection
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}");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.
Sourcepub async fn set_download_path(
&self,
dir: impl Into<PathBuf>,
) -> Result<(), ZendriverError>
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?;Sourcepub async fn create_browser_context_with(
&self,
proxy_server: Option<&str>,
proxy_bypass_list: Option<&str>,
) -> Result<BrowserContext, ZendriverError>
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.
Sourcepub async fn create_browser_context(
&self,
) -> Result<BrowserContext, ZendriverError>
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.
Sourcepub async fn new_tab(&self) -> Result<Tab, ZendriverError>
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:
- Sends
Target.createTarget { url: "about:blank" }at browser scope (no session_id) — the response includes the newtargetId. - Polls the internal tabs registry every 50ms for up to 5s, looking
for a
TabwhoseTab::target_idmatches. The tab registrar populates that entry asynchronously when theTarget.attachedToTargetevent arrives. - Returns the matching
Tabon 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?;Sourcepub async fn new_tab_at(
&self,
url: impl Into<String>,
) -> Result<Tab, ZendriverError>
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?;Sourcepub async fn new_window(&self) -> Result<Tab, ZendriverError>
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?;Sourcepub async fn new_window_at(
&self,
url: impl Into<String>,
) -> Result<Tab, ZendriverError>
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?;Sourcepub async fn tabs(&self) -> Vec<Tab>
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?);
}Sourcepub async fn tab_count(&self) -> usize
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);Sourcepub async fn reconnect(&self) -> Result<(), ZendriverError>
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 treatmain_tab()as invalid after a reconnect and usetabs().
§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?;
}Sourcepub async fn close(self) -> Result<(), ZendriverError>
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?;Sourcepub async fn grant_permissions(
&self,
perms: &[PermissionType],
origin: Option<&str>,
) -> Result<(), ZendriverError>
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?;Sourcepub async fn grant_all_permissions(&self) -> Result<(), ZendriverError>
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?;Sourcepub async fn reset_permissions(&self) -> Result<(), ZendriverError>
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?;