Skip to content

browser

stable

Offscreen Chromium browser plugin (CEF) providing a Browser class that renders pages to BGRA pixel buffers, with full navigation, JavaScript execution, input injection, zoom, and DevTools control.

use plugin browser::{Browser.new, pump_messages, load_url, …}
37 functions Web
/ filter jk navigate Esc clear
Functions (37)
  1. Browser.new Create an offscreen browser from a config table
  2. pump_messages Pump the CEF message loop (static, call every frame)
  3. load_url Navigate the browser to a URL
  4. load_html Load an HTML string directly into the page
  5. get_url Get the current page URL
  6. get_title Get the current page title
  7. navigate_back Go back in the navigation history
  8. navigate_forward Go forward in the navigation history
  9. can_navigate_back Check if back navigation is possible
  10. can_navigate_forward Check if forward navigation is possible
  11. reload Reload the current page
  12. stop_loading Stop loading the current page
  13. is_loading Check whether the page is still loading
  14. get_pixels Get the latest rendered frame as BGRA bytes
  15. get_size Get the browser viewport size
  16. resize Resize the browser viewport
  17. set_rendering_paused Pause or resume offscreen rendering
  18. is_rendering_paused Check if rendering is paused
  19. execute_js Execute JavaScript in the page's main frame
  20. inject_mouse_move Inject a mouse move event
  21. inject_mouse_down Inject a mouse button press
  22. inject_mouse_up Inject a mouse button release
  23. inject_mouse_wheel Inject a mouse wheel scroll event
  24. inject_key_down Inject a key press event
  25. inject_key_up Inject a key release event
  26. inject_key_char Inject a character key event
  27. inject_text Insert a text string via IME commit
  28. focus Give keyboard focus to the browser
  29. is_focused Check if the browser has focus
  30. set_muted Mute or unmute page audio
  31. set_zoom_level Set the page zoom level
  32. get_zoom_level Get the current zoom level
  33. open_devtools Open the Chromium DevTools window
  34. close_devtools Close the DevTools window
  35. toggle_devtools Toggle the DevTools window
  36. has_devtools Check if DevTools is open
  37. close Destroy the browser and release its handle

Overview

The browser plugin embeds a headless Chromium engine (via CEF) that renders web pages entirely offscreen into a raw BGRA pixel buffer. Instead of opening a real window, you create a Browser from a config table and receive a handle; every operation — navigation, JavaScript, input injection, zoom, DevTools — is a method on that handle. The rendered frame is pulled out with get_pixels as bytes you can upload to a GPU texture, making this the building block for in-app web views, web-to-texture effects, and automated screenshotting.

Chromium is single-threaded and message-loop driven: nothing loads or repaints until you pump its loop. Call the static Browser.pump_messages regularly (typically once per render frame), and use is_loading to know when navigation has settled. Handles are stateful — held mouse buttons and modifier keys persist across inject_* calls so that presses become drags and Shift/Control/Alt stay down — and each browser frees its native resources when you close it or when it is garbage collected.

Common patterns

Open a page, wait for it to finish loading, then grab the rendered frame as bytes:

use plugin browser::{Browser}

let b = Browser.new(#{ width: 1280, height: 720, url: "https://example.com" })
while b.is_loading() {
  Browser.pump_messages()
}
let frame = b.get_pixels()
if frame != nil {
  print("rendered {b.get_size()["width"]}x{b.get_size()["height"]} -> {frame.len()} bytes")
}
b.close()

Render generated HTML to a texture buffer without any web server:

use plugin browser::{Browser}

let b = Browser.new(#{ width: 640, height: 360 })
b.load_html("<body style='background:teal'><h1>Hello from Zolo</h1></body>")
while b.is_loading() {
  Browser.pump_messages()
}
let pixels = b.get_pixels()
print("html frame: {pixels.len()} bytes")
b.close()

Drive the page like a user: focus it, click a coordinate, and type into the focused field:

use plugin browser::{Browser}

let b = Browser.new(#{ width: 800, height: 600, url: "https://example.com" })
while b.is_loading() {
  Browser.pump_messages()
}
b.focus()
b.inject_mouse_down(200, 150, "left")
b.inject_mouse_up(200, 150, "left")
b.inject_text("hello from zolo")
Browser.pump_messages()
b.close()

Create an offscreen browser from a config table

Creates an offscreen Chromium browser and returns a Browser handle. The config table accepts width (default 800), height (default 600), url (default "about:blank"), and optionally html to load an inline HTML string instead of fetching a URL. The page renders into an internal BGRA pixel buffer that you read with get_pixels.

use plugin browser::{Browser}

let b = Browser.new(#{ width: 1280, height: 720, url: "https://example.com" })
print("created: {b.get_url()}")

Pump the CEF message loop (static, call every frame)

Static method that pumps the CEF message loop once. Chromium does network, layout, and paint work inside this loop, so call it regularly (typically once per frame) or the page will never load or repaint.

use plugin browser::{Browser}

let b = Browser.new(#{ url: "https://example.com" })
while b.is_loading() {
  Browser.pump_messages()
}
print("loaded: {b.get_url()}")

Navigate the browser to a URL

Navigates the browser's main frame to url. Navigation is asynchronous — keep pumping messages and check is_loading to know when it finishes.

use plugin browser::{Browser}

let b = Browser.new(#{ width: 800, height: 600 })
b.load_url("https://zolo-lang.dev")

Reuse a single browser to visit several pages in sequence, draining the load between each:

use plugin browser::{Browser}

let b = Browser.new(#{ width: 1024, height: 768 })
for url in ["https://example.com", "https://zolo-lang.dev"] {
  b.load_url(url)
  while b.is_loading() {
    Browser.pump_messages()
  }
  print("loaded: {b.get_url()}")
}

Load an HTML string directly into the page

Loads an HTML string directly into the main frame (internally encoded as a data:text/html URL). Useful for rendering generated UI without a web server.

use plugin browser::{Browser}

let b = Browser.new(#{ width: 640, height: 480 })
b.load_html("<h1 style='color: teal'>Hello from Zolo</h1>")

Get the current page URL

Returns the URL currently loaded in the main frame, or an empty string if no page is loaded yet.

use plugin browser::{Browser}

let b = Browser.new(#{ url: "https://example.com" })
print("current: {b.get_url()}")

Get the current page title

Returns the current page title. Title tracking is not fully implemented yet, so this currently returns an empty string.

Check if back navigation is possible

Returns true if there is a previous entry in the navigation history.

Check if forward navigation is possible

Returns true if there is a next entry in the navigation history.

Reload the current page

Reloads the current page.

use plugin browser::{Browser}

let b = Browser.new(#{ url: "https://example.com" })
b.reload()

Stop loading the current page

Stops loading the current page, like pressing the browser's stop button.

Check whether the page is still loading

Returns true while the page is still loading. Combine with pump_messages to wait for a page to finish.

use plugin browser::{Browser}

let b = Browser.new(#{ url: "https://example.com" })
while b.is_loading() {
  Browser.pump_messages()
}
print("done loading")

Get the latest rendered frame as BGRA bytes

Pumps the message loop and returns the most recent rendered frame as raw BGRA bytes (width * height * 4), or nil if no frame has been painted yet. Upload the buffer to a GPU texture for offscreen rendering.

use plugin browser::{Browser}

let b = Browser.new(#{ width: 800, height: 600, url: "https://example.com" })
while b.is_loading() {
  Browser.pump_messages()
}
let pixels = b.get_pixels()
if pixels != nil {
  print("got frame: {pixels.len()} bytes")
}

After a resize, pump a few times so Chromium relayouts and repaints before reading the new-sized frame:

use plugin browser::{Browser}

let b = Browser.new(#{ width: 800, height: 600, url: "https://example.com" })
b.resize(1920, 1080)
for _ in 0..5 {
  Browser.pump_messages()
}
let frame = b.get_pixels()
print("hi-res frame: {frame.len()} bytes")

Get the browser viewport size

Returns the current viewport size as a table with width and height fields.

use plugin browser::{Browser}

let b = Browser.new(#{ width: 1024, height: 768 })
let size = b.get_size()
print("{size["width"]}x{size["height"]}")

Resize the browser viewport

Resizes the browser viewport. The page relayouts and subsequent get_pixels frames use the new dimensions.

use plugin browser::{Browser}

let b = Browser.new(#{ width: 800, height: 600 })
b.resize(1920, 1080)

Pause or resume offscreen rendering

Pauses or resumes offscreen rendering. While paused, new frames are not produced — useful to save CPU when the browser content is not visible.

use plugin browser::{Browser}

let b = Browser.new(#{ url: "https://example.com" })
b.set_rendering_paused(true)
print("paused: {b.is_rendering_paused()}")
b.set_rendering_paused(false)

Check if rendering is paused

Returns true if offscreen rendering is currently paused.

Execute JavaScript in the page's main frame

Executes a JavaScript string in the page's main frame. Fire-and-forget: the return value of the script is not propagated back to Zolo.

use plugin browser::{Browser}

let b = Browser.new(#{ url: "https://example.com" })
b.execute_js("document.body.style.background = 'black'")

Inject a mouse move event

Injects a mouse move event at viewport coordinates (x, y). Held buttons and modifier keys from previous inject calls are carried along, so move events during a press become drags.

use plugin browser::{Browser}

let b = Browser.new(#{ url: "https://example.com" })
b.inject_mouse_move(200, 150)

Inject a mouse button press

Injects a mouse button press at (x, y). button is "left", "right", or "middle" (defaults to "left"). The button stays held until a matching inject_mouse_up.

use plugin browser::{Browser}

let b = Browser.new(#{ url: "https://example.com" })
b.inject_mouse_down(200, 150, "left")
b.inject_mouse_up(200, 150, "left")

Inject a mouse button release

Injects a mouse button release at (x, y), completing a click started with inject_mouse_down.

Inject a mouse wheel scroll event

Injects a mouse wheel event at (x, y) with horizontal delta dx and vertical delta dy (positive dy scrolls up).

use plugin browser::{Browser}

let b = Browser.new(#{ url: "https://example.com" })
b.inject_mouse_wheel(400, 300, 0, -120)

Inject a key press event

Injects a key press. key accepts winit-style names ("A", "Enter", "ArrowDown", "F5", "Shift", ...). Modifier keys (Shift, Control, Alt) stay held for subsequent events until released. Pressing "F12" is intercepted and toggles DevTools instead.

use plugin browser::{Browser}

let b = Browser.new(#{ url: "https://example.com" })
b.focus()
b.inject_key_down("Enter")
b.inject_key_up("Enter")

Modifier keys stay held until released, so you can build chords like Ctrl+A. Press the modifier first, the target key, then release in reverse order:

use plugin browser::{Browser}

let b = Browser.new(#{ url: "https://example.com" })
b.focus()
b.inject_key_down("Control")
b.inject_key_down("A")
b.inject_key_up("A")
b.inject_key_up("Control")

Inject a key release event

Injects a key release for key, clearing any modifier state it was holding.

Inject a character key event

Injects a character event for text input fields. For typing visible characters, send this between key down and key up, or use inject_text for whole strings.

use plugin browser::{Browser}

let b = Browser.new(#{ url: "https://example.com" })
b.focus()
b.inject_key_char("z")

Insert a text string via IME commit

Inserts an entire text string into the focused element via an IME commit. The simplest way to type into input fields.

use plugin browser::{Browser}

let b = Browser.new(#{ url: "https://example.com" })
b.focus()
b.inject_text("hello from zolo")

Give keyboard focus to the browser

Gives keyboard focus to the browser so injected key and text events reach the page.

Check if the browser has focus

Returns true if the browser currently has focus (set via focus).

Mute or unmute page audio

Mutes or unmutes all audio playing in the page.

use plugin browser::{Browser}

let b = Browser.new(#{ url: "https://example.com" })
b.set_muted(true)

Set the page zoom level

Sets the page zoom level. 0.0 is the default zoom; each unit is roughly a 20% step (Chromium zoom-level semantics), so 1.0 zooms in and -1.0 zooms out.

use plugin browser::{Browser}

let b = Browser.new(#{ url: "https://example.com" })
b.set_zoom_level(1.0)
print("zoom: {b.get_zoom_level()}")

Negative levels zoom out, and 0.0 resets to the default — handy for fitting more content into a fixed-size offscreen surface:

use plugin browser::{Browser}

let b = Browser.new(#{ url: "https://example.com" })
b.set_zoom_level(-2.0)
print("zoomed out to {b.get_zoom_level()}")
b.set_zoom_level(0.0)

Get the current zoom level

Returns the current zoom level (0.0 is the default).

Open the Chromium DevTools window

Opens the Chromium DevTools in a separate window attached to this browser.

use plugin browser::{Browser}

let b = Browser.new(#{ url: "https://example.com" })
b.open_devtools()

Close the DevTools window

Closes the DevTools window if it is open.

Toggle the DevTools window

Opens DevTools if closed, closes it if open. Equivalent to injecting an "F12" key press.

Check if DevTools is open

Returns true if a DevTools window is currently open for this browser.

use plugin browser::{Browser}

let b = Browser.new(#{ url: "https://example.com" })
b.toggle_devtools()
print("devtools open: {b.has_devtools()}")

Destroy the browser and release its handle

Destroys the browser instance and releases its handle. The handle becomes invalid; any further method calls on it will error. Browsers are also cleaned up automatically when garbage collected.

use plugin browser::{Browser}

let b = Browser.new(#{ url: "https://example.com" })
b.close()
enespt-br