browser
stableOffscreen 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, …} Functions (37)
- Browser.new Create an offscreen browser from a config table
- pump_messages Pump the CEF message loop (static, call every frame)
- load_url Navigate the browser to a URL
- load_html Load an HTML string directly into the page
- get_url Get the current page URL
- get_title Get the current page title
- navigate_back Go back in the navigation history
- navigate_forward Go forward in the navigation history
- can_navigate_back Check if back navigation is possible
- can_navigate_forward Check if forward navigation is possible
- reload Reload the current page
- stop_loading Stop loading the current page
- is_loading Check whether the page is still loading
- get_pixels Get the latest rendered frame as BGRA bytes
- get_size Get the browser viewport size
- resize Resize the browser viewport
- set_rendering_paused Pause or resume offscreen rendering
- is_rendering_paused Check if rendering is paused
- execute_js Execute JavaScript in the page's main frame
- inject_mouse_move Inject a mouse move event
- inject_mouse_down Inject a mouse button press
- inject_mouse_up Inject a mouse button release
- inject_mouse_wheel Inject a mouse wheel scroll event
- inject_key_down Inject a key press event
- inject_key_up Inject a key release event
- inject_key_char Inject a character key event
- inject_text Insert a text string via IME commit
- focus Give keyboard focus to the browser
- is_focused Check if the browser has focus
- set_muted Mute or unmute page audio
- set_zoom_level Set the page zoom level
- get_zoom_level Get the current zoom level
- open_devtools Open the Chromium DevTools window
- close_devtools Close the DevTools window
- toggle_devtools Toggle the DevTools window
- has_devtools Check if DevTools is open
- 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.
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()