Skip to main content

Page

Struct Page 

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

Wrapper around rustenium’s FirefoxBrowser.

Implementations§

Source§

impl Page

Source

pub async fn launch(config: Option<FoxBrowserConfig>) -> Result<Self>

Launch a new Firefox instance and return its first page.

Source

pub async fn goto(&self, url: &str) -> Result<()>

Navigate the active browsing context to url.

Source

pub async fn evaluate( &self, expr: impl Into<String>, ) -> Result<EvaluationResult>

Evaluate a JavaScript expression in the active context.

The returned value is NOT promise-awaited: if expr evaluates to a Promise, the opaque promise handle is returned, not its resolved value. Use Self::evaluate_await for expressions that may be async.

Source

pub async fn evaluate_await( &self, expr: impl Into<String>, ) -> Result<EvaluationResult>

Evaluate a JavaScript expression, awaiting the result if it is a Promise.

This sets the BiDi awaitPromise flag, so an expression that returns a Promise resolves to its fulfilled value before serialization. A non-promise expression is returned unchanged, so this is a safe superset of Self::evaluate for any caller that wants the resolved value (e.g. surfaces backed by MediaCapabilities.decodingInfo, Worker/ ServiceWorker message round-trips, or any async probe).

Source

pub async fn evaluate_in_context( &self, expr: impl Into<String>, context: &FrameId, ) -> Result<EvaluationResult>

Evaluate in a specific browsing context (frame).

Source

pub async fn find_element(&self, selector: &str) -> Result<Element>

Find the first element matching selector.

Source

pub async fn find_elements(&self, selector: &str) -> Result<Vec<Element>>

Find all elements matching selector.

Source

pub async fn set_files(&self, selector: &str, files: Vec<String>) -> Result<()>

Set the file(s) on a <input type=file> element via BiDi input.setFiles.

This is the trusted file-upload primitive: it attaches real local files to the input the same way a human’s file picker does (no synthetic events), so the entire file-upload attack surface, path-traversal filenames, content-type bypass, SVG/XML XXE, RCE-via-upload, SSRF (becomes testable). selector must resolve to the file input; files are absolute local paths.

Source

pub async fn screenshot(&self) -> Result<Vec<u8>>

Capture a viewport screenshot and return raw PNG bytes.

Source

pub async fn reload(&self) -> Result<()>

Reload the active context.

Source

pub async fn url(&self) -> Result<String>

Current URL of the active context.

Source

pub async fn title(&self) -> Result<String>

Document title of the active context.

Source

pub async fn frames(&self) -> Result<Vec<FrameId>>

List all browsing-context IDs (main page + every iframe).

Source

pub async fn mainframe(&self) -> Result<Option<FrameId>>

Return the active (main) browsing context.

Source

pub async fn frame_execution_context( &self, frame_id: FrameId, ) -> Result<Option<FrameId>>

Verify a browsing context still exists.

Source

pub async fn list_frames(&self) -> Result<Vec<FrameInfo>>

List every browsing context (main document + all iframes) with the metadata an agent needs to target one: opaque id, current url, window.name, and whether it is the main frame.

This is the discovery primitive for cross-origin iframe interaction embedded apps, OAuth/payment widgets, postMessage surfaces, captcha tiles. Pass a returned id back as the frame target to Page::eval_in_frame / Page::click_in_frame / Page::type_in_frame.

Source

pub async fn frame_tree(&self) -> Result<Vec<FrameTreeNode>>

Walk the live frame tree via WebDriver BiDi browsingContext.getTree.

Returns every browsing context, the main document plus every nested iframe at any origin, in pre-order (a parent always precedes its children), each carrying true parent linkage, committed URL, and depth.

This is the structural primitive Page::frames cannot provide: that method flattens the tree to a bare id list, discarding which iframe is nested inside which. Cross-origin captcha challenges (reCAPTCHA’s bframe inside its anchor, an hCaptcha challenge inside its checkbox) are exactly the topologies that flattening destroys, so the solver must re-derive structure every pass. getTree recovers it in one round-trip, and, unlike reading document.URL from inside each frame, reports the URL of a cross-origin frame the browser knows but in-frame JS may not yet expose.

Source

pub async fn resolve_frame(&self, spec: &str) -> Result<FrameId>

Resolve a frame target spec to a concrete FrameId, polling briefly so an iframe that attaches asynchronously (captcha widgets, lazy embeds, post-navigation frames) is found rather than racing to a “no such frame”.

Accepts every shape an agent naturally has on hand, so it never has to call list_frames first:

  • exact browsing-context id (from Page::list_frames)
  • index:<n> or a bare 0-based integer into the frame list
  • url:<substr>: first frame whose URL contains the substring
  • name:<name>: first frame whose window.name equals it
  • any other string, tried as an exact id, then as a URL substring
  • empty / main / top → the main document
Source

pub async fn resolve_frame_within( &self, spec: &str, timeout: Duration, ) -> Result<FrameId>

Page::resolve_frame with an explicit overall timeout for the attach poll. timeout of zero means a single attempt.

Source

pub async fn eval_in_frame( &self, spec: &str, expr: impl Into<String>, ) -> Result<EvaluationResult>

Evaluate expr inside the frame named by spec (id, index, or main/top). Full read/write JS runs in that frame’s own context, so the agent can read or mutate a cross-origin iframe’s DOM, drive postMessage, or land a DOM-XSS PoC inside an embedded document.

Source

pub async fn click_in_frame(&self, spec: &str, selector: &str) -> Result<()>

TRUSTED click on selector inside the frame named by spec.

Resolves the element’s centre in the frame’s own viewport, then dispatches a real BiDi pointer event in that context via Page::click_at_in, so event.isTrusted is true even for a cross-origin iframe. Returns an error if the selector matches nothing visible in the frame.

Source

pub async fn type_in_frame( &self, spec: &str, selector: &str, text: &str, ) -> Result<()>

Focus selector inside the frame named by spec and type text into it with human-like timing. The keystrokes are dispatched in the frame’s own context so they land in the cross-origin iframe’s focused element.

Source

pub async fn start_dialog_log(&self) -> Result<DialogLog>

Start capturing JS dialogs and page-initiated downloads via BiDi browsingContext.* events. Returns a crate::dialog::DialogLog handle (cheap to clone) that accumulates events for the life of the page.

This is how the agent confirms alert-based XSS (the alert() message is recorded even when the prompt auto-handles, so there is no hang), reads confirm/prompt text, and inspects downloads. Pair with Page::handle_user_prompt to answer a prompt left open by the ignore handler. Mirrors Page::start_network_log.

Source

pub async fn start_sensors(&self) -> Result<String>

Install the passive instrumentation grid (see crate::sensors) so the page reports DOM-XSS sink writes, console output, uncaught errors, CSP violations, and inbound postMessage on its own.

Injected twice: as a preload (runs in the MAIN world before page scripts on every future navigation) AND evaluated once on the current document so a page already loaded at launch is covered. The script is idempotent, so the double-install is safe. Read what it captured with Page::read_signals. Mirrors Page::start_network_log.

Source

pub async fn read_signals(&self, clear: bool) -> Result<Value>

Read the captured signal buffer. With clear true the buffer is emptied after the snapshot so the next read returns only NEW signals (deltas) the basis for “what did my last action trigger?” telemetry.

Source

pub async fn handle_user_prompt( &self, context: Option<&FrameId>, accept: bool, user_text: Option<&str>, ) -> Result<()>

Answer an open JS user prompt in context (or the active frame when None): accept true clicks OK / accepts beforeunload; user_text fills a prompt() box before accepting. Only effective when the page was launched with the ignore prompt handler (otherwise Firefox auto-handles the prompt before this runs). Mirrors the Page::set_files command path.

Source

pub async fn mouse_move_human( &self, x0: f64, y0: f64, x1: f64, y1: f64, ) -> Result<()>

Move the mouse from (x0, y0) to (x1, y1) using human-like curves.

Source

pub async fn mouse_down(&self, x: f64, y: f64) -> Result<()>

Mouse-down at (x, y) in the active context.

Source

pub async fn mouse_up(&self, _x: f64, _y: f64) -> Result<()>

Mouse-up at (x, y) in the active context.

Source

pub async fn click_at(&self, x: f64, y: f64) -> Result<()>

Click at (x, y) in the active (top-level) context with realistic press/release timing.

NOTE: for a target inside a cross-origin iframe (the production captcha case: Turnstile/hCaptcha/reCAPTCHA all render their checkbox in an OOPIF), prefer Page::click_at_in with the iframe’s context. A pointer action dispatched in the top context does not reliably route across a Fission process boundary, which is why a top-context viewport click on a captcha checkbox silently fails to deliver.

Source

pub async fn click_at_in(&self, context: &FrameId, x: f64, y: f64) -> Result<()>

Click at (x, y) within a SPECIFIC browsing context.

This is the cross-origin-correct click path: BiDi input.performActions is dispatched in context, so the trusted pointer event is delivered into that frame’s content process. For a cross-origin iframe checkbox, pass the iframe’s FrameId (from Page::frames) with coordinates in that frame’s own viewport space (origin at the iframe’s top-left). Because the event is real BiDi input (not a synthetic JS MouseEvent), event.isTrusted is true: the property every modern captcha gates its checkbox on.

Source

pub async fn move_mouse_to(&self, x: f64, y: f64) -> Result<()>

Move the pointer to an absolute viewport coordinate as a single TRUSTED BiDi input.performActions PointerMove (no synthetic JS MouseEvent).

This is the trusted primitive that human-trajectory generators must dispatch each interpolated point through. A document.dispatchEvent(new MouseEvent('mousemove', …)) produces isTrusted === false, which every modern anti-bot scorer flags on sight, so a beautifully shaped but JS-dispatched path is worse than useless. Routing each point through here makes the whole trajectory trusted and lets it cross into cross-origin frames by viewport hit-test.

Source

pub async fn scroll(&self, dx: i64, dy: i64) -> Result<()>

Scroll the wheel at the current mouse position.

Source

pub async fn scroll_realistic( &self, direction: ScrollDirection, amount: u32, ) -> Result<()>

Human-like scroll (smooth easing with noise).

Source

pub async fn type_text(&self, text: &str) -> Result<()>

Type text into the active context with human-like delays.

Source

pub async fn key_down(&self, key: &str) -> Result<()>

Press a key down in the active context.

Source

pub async fn key_up(&self, key: &str) -> Result<()>

Release a key in the active context.

Source

pub async fn key_press(&self, key: &str) -> Result<()>

Press and release a key in the active context.

Source

pub async fn add_preload_script(&self, source: &str) -> Result<String>

Inject a preload script that runs in the page’s main world before any page script, on every new document.

source is a SCRIPT BODY (statements), matching CDP’s Page.addScriptToEvaluateOnNewDocument semantics. WebDriver BiDi’s script.addPreloadScript instead takes a functionDeclaration that it invokes as a function, so a bare body, or a self-invoking IIFE like (() => {…})() (which evaluates to undefined, not a callable), is silently never run, nullifying the script. We therefore wrap the body in an arrow function here so callers can pass a plain body and have it actually execute. This is the single point that made guise’s stealth preloads (all written as IIFE bodies) no-ops.

Source

pub async fn get_cookies(&self) -> Result<Vec<CapturedCookie>>

Capture all cookies (including HttpOnly) via BiDi storage.getCookies.

Set a cookie via BiDi storage.setCookie.

Source

pub fn profile_dir(&self) -> Option<&str>

Return the Firefox profile directory path, if known.

Source

pub async fn start_network_log(&self) -> Result<NetworkLog>

Start capturing all network traffic (requests + responses) via BiDi.

Returns a crate::network::NetworkLog handle that can be queried at any time while the browser is alive. The log is shared (Clone is cheap) and accumulates events until the page is closed.

§Example
let log = page.start_network_log().await?;
page.goto("https://example.com").await?;
let entries = log.entries().await;
let tokens = log.extract_tokens().await;
Source

pub async fn close(&self) -> Result<()>

Close the browser. For a self-managed (Remote-attach) child this performs a CLEAN quit so the profile’s localStorage / IndexedDB / cookies are flushed to disk before exit; capped so a hung engine still tears down.

Persistence depends on this being called, a dropped Page (see Drop) can only best-effort SIGTERM/SIGKILL, which does NOT flush localStorage on this engine, so a reused profile_dir would lose recent writes.

Trait Implementations§

Source§

impl Drop for Page

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

§

impl !Freeze for Page

§

impl !RefUnwindSafe for Page

§

impl !UnwindSafe for Page

§

impl Send for Page

§

impl Sync for Page

§

impl Unpin for Page

§

impl UnsafeUnpin for Page

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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

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> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
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