Obscurax.Page (obscurax v0.1.0)

Copy Markdown View Source

A browser tab/page — navigation, V8 evaluation, element ops, and callbacks.

Each page runs on a dedicated OS thread with its own current_thread tokio runtime and V8 isolate. The synchronous-feeling API blocks the caller while async operations run on the page thread; V8 evaluation is scheduled on dirty CPU schedulers so it never blocks the BEAM.

Navigation and evaluation

:ok = Obscurax.Page.goto(page, "https://example.com")
{:ok, title} = Obscurax.Page.evaluate(page, "document.title")
{:ok, html} = Obscurax.Page.content(page)

Element operations

Element operations take a node_id returned by query_selector/2 or wait_for_selector/3:

{:ok, node_id} = Obscurax.Page.query_selector(page, "h1")
{:ok, text} = Obscurax.Page.element_text(page, node_id)
:ok = Obscurax.Page.element_click(page, node_id)

Request observers

{:ok, _pid} = Obscurax.Page.on_request(page, fn req ->
  IO.inspect(req.url)
end)

Request interception

:ok = Obscurax.Page.enable_interception(page)
receive do
  {:obscurax_intercept, ref, %{url: url}} ->
    :ok = Obscurax.reply_intercept(page, ref, :continue)
end

Summary

Types

t()

@type t() :: %Obscurax.Page{ref: reference()}

Functions

add_preload_script(page, script)

@spec add_preload_script(t(), String.t()) :: :ok | {:error, Obscurax.Error.t()}

close(page)

@spec close(t()) :: :ok

content(page)

@spec content(t()) :: {:ok, String.t()} | {:error, Obscurax.Error.t()}

element_attribute(page, node_id, name)

@spec element_attribute(t(), non_neg_integer(), String.t()) ::
  {:ok, String.t() | nil} | {:error, Obscurax.Error.t()}

element_click(page, node_id)

@spec element_click(t(), non_neg_integer()) :: :ok | {:error, Obscurax.Error.t()}

element_text(page, node_id)

@spec element_text(t(), non_neg_integer()) ::
  {:ok, String.t()} | {:error, Obscurax.Error.t()}

enable_interception(page)

@spec enable_interception(t()) :: :ok | {:error, Obscurax.Error.t()}

evaluate(page, expr)

@spec evaluate(t(), String.t()) :: {:ok, term()} | {:error, Obscurax.Error.t()}

evaluate!(page, expr)

@spec evaluate!(t(), String.t()) :: term()

goto(page, url, opts \\ [])

@spec goto(t(), String.t(), keyword()) :: :ok | {:error, Obscurax.Error.t()}

goto!(page, url, opts \\ [])

@spec goto!(t(), String.t(), keyword()) :: :ok

on_request(page, fun)

@spec on_request(t(), (map() -> term())) ::
  {:ok, pid()} | {:error, Obscurax.Error.t()}

on_response(page, fun)

@spec on_response(t(), (map() -> term())) ::
  {:ok, pid()} | {:error, Obscurax.Error.t()}

Register a response observer. The callback receives a map with :url, :method, :resource_type, and :status for each response.

query_selector(page, selector)

@spec query_selector(t(), String.t()) ::
  {:ok, non_neg_integer() | nil} | {:error, Obscurax.Error.t()}

settle(page, max_ms)

@spec settle(t(), non_neg_integer()) :: :ok | {:error, Obscurax.Error.t()}

url(page)

@spec url(t()) :: {:ok, String.t()} | {:error, Obscurax.Error.t()}

wait_for_selector(page, selector, timeout_ms)

@spec wait_for_selector(t(), String.t(), non_neg_integer()) ::
  {:ok, non_neg_integer()} | {:error, Obscurax.Error.t()}

wait_for_selector!(page, selector, timeout_ms)

@spec wait_for_selector!(t(), String.t(), non_neg_integer()) :: non_neg_integer()