The rampart-chromeview module¶
Preface¶
Acknowledgment¶
The rampart-chromeview module drives an external Chromium (or Google Chrome) process via the Chrome DevTools Protocol. The authors of Rampart extend their thanks to the Chromium project for the browser engine and the documented DevTools Protocol, and to the Puppeteer project, whose well-shaped API surface is the model on which rampart-chromeview’s own surface is patterned. The WebSocket connection through which CDP traffic flows is provided by the rampart-net module.
License¶
The rampart-chromeview module is released under the MIT license.
What does it do?¶
The rampart-chromeview module provides programmatic automation control over a real Chromium browser running as a separate process. The exposed API covers navigation, DOM queries, JavaScript evaluation inside the page, screenshots, PDF generation, cookies, network interception, mouse and keyboard event dispatch, iframe traversal, multiple tabs, incognito-like browser contexts, page-to-host function bindings, drag-and-drop, and device emulation.
The rampart-webview module — which embeds a native webview library in-process — is intended for building desktop GUI applications. rampart-chromeview is the complementary module for the inverse case: a separate Chromium process driven over CDP for automation, scraping, testing, server-side rendering, and headless PDF or screenshot generation.
Typical applications include:
- Server-side rendering of a single-page application, capturing the finished HTML.
- PDF generation from HTML and CSS sources.
- Web scraping of JavaScript-rendered pages.
- Screenshot automation, including device emulation and full-page captures.
- Headless browser testing.
- Data extraction from sites whose content is gated behind a single-page-application router.
How does it work?¶
After the module is loaded, a Chromium process is launched (or an existing one connected to)
via the launch() or
connect() function.
All subsequent control flows over the Chrome DevTools Protocol on a WebSocket.
Rampart’s libevent loop does not pump during top-level blocking calls, so a single-threaded
design could not satisfy synchronous calls — the CDP reply would never arrive while the main
loop is parked. rampart-chromeview addresses this by running the WebSocket and the CDP
dispatcher in a worker thread via rampart.thread. Main-thread synchronous calls block on rampart.thread.waitfor; the
worker unblocks them via rampart.thread.put once the reply arrives.
The same API surface supports three calling conventions — synchronous blocking calls, classic
callbacks, and async/await Promises — selected automatically per call based on the arguments and
the runtime mode.
Loading and Using the Module¶
Prerequisites¶
-
Chromium or Google Chrome installed on the host. The module searches the following locations by default:
-
/usr/bin/google-chrome-stable -
/usr/bin/google-chrome -
/usr/bin/chromium -
/usr/bin/chromium-browser -
/snap/bin/chromium(modern Ubuntu’s Snap-packaged Chromium) -
/usr/local/bin/chrome(FreeBSD’spkg install chromium) -
/usr/local/bin/chromium(FreeBSD, alternate name) -
/Applications/Google Chrome.app/Contents/MacOS/Google Chrome -
/Applications/Chromium.app/Contents/MacOS/Chromium
Passing
{executablePath: "..."}tolaunch()overrides the search.On Debian/Ubuntu:
sudo apt install chromium
On FreeBSD:
pkg install chromium
-
-
Rampart built with rampart-net and rampart-curl (both ship in the standard distribution).
Loading¶
Loading the module is a simple matter of using the require() function:
var chrome = require("rampart-chromeview");
Quick Start¶
A complete script that launches headless Chrome, navigates to a page, captures the title, and writes a full-page PNG screenshot:
rampart.globalize(rampart.utils);
var chrome = require("rampart-chromeview");
var browser = chrome.launch({headless: true});
var page = browser.newPage();
page.goto("https://example.com", {waitUntil: "load"});
printf("title: %s\n", page.title());
var png = page.screenshot({fullPage: true});
fprintf("/tmp/example.png", "%s", png);
browser.close();
Calling Conventions¶
Every CDP-backed method supports three calling styles, selected automatically by the module at call time.
Synchronous¶
When no callback is supplied and the transpiler is not in effect, the call blocks and returns the result directly:
var title = page.title();
Callback¶
When the last argument is a function, the call returns immediately and the callback fires on
the main event loop once the CDP reply arrives. The callback signature is (result, error):
page.title(function(title, err) {
if (err) throw err;
console.log(title);
});
Promise / async-await¶
When no callback is supplied and a source transformation is active for the current script, the call returns a Promise. Both of rampart’s source transformations are honoured:
- The built-in transpiler, engaged by
-t,"use transpiler", or"use transpilerGlobally". - Babel, engaged by
-b,"use babel", or"use babelGlobally".
"use transpiler"
async function main() {
const title = await page.title();
}
The module detects the active transformation at call time by inspecting internal markers that
rampart sets when either path is engaged. Promise itself is installed by
rampart unconditionally and is therefore not a useful discriminator.
A single source file may mix synchronous calls and callbacks freely; transformed code may
additionally use await. Within a file that has a source transformation active, supplying a
callback as the last argument still selects the callback path — the dispatcher prefers an
explicit callback over Promise return when both would be valid.
Page-side callbacks: strings always work; functions require “use transpiler”¶
Page-side callbacks passed to evaluate, $eval, $$eval, waitForFunction, ElementHandle.evaluate, and similar methods may be supplied in either form:
- A source string — accepted in every runtime mode and requires no transpiler.
-
A JavaScript function — accepted only under
"use transpiler", which causesFunction.prototype.toString()to return the original source. Without the transpiler, the underlying engine returns the stub"function () { [ecmascript code] }"and the function body cannot be shipped to Chrome. The module raises an error in that case.
/* String — accepted in every mode */
page.$eval("h1", "el => el.textContent");
/* Function — accepted only under "use transpiler" */
"use transpiler"
await page.$eval("h1", el => el.textContent);
await page.evaluate(() => document.title);
Event-driven features need the main event loop¶
page.exposeFunction(name,
fn) and page.on("request", ...) invoke the Rampart-side
handler from the main thread’s event loop. The event loop runs only after the top-level
script finishes (or between awaits in transpiled mode). Queued handler invocations will not fire until
the chain of top-level blocking synchronous calls returns.
In practice this means that for exposeFunction and request interception, either (a) the script must run in
"use transpiler" mode with async/await, or (b) setup is performed
at the top level and the event loop is then allowed to run.
Known Limitations¶
The behaviors documented in this section are areas where rampart-chromeview deliberately diverges from Puppeteer or carries a caveat that is worth noting.
Sync mode and event-triggered waits do not mix¶
page.waitForResponse(urlMatch), waitForRequest, waitForFileChooser, waitForNavigation, and the page
event handlers all rely on the main event loop running. In sync mode the calling thread is
blocked, so if the action that triggers the event runs on the same thread, after the
wait call, the call will deadlock:
/* Will deadlock — sync mode blocks the thread, so the fetch
never runs. */
var resp = page.waitForResponse("/api/x");
page.evaluate("fetch('/api/x')");
The remedies are: use "use
transpiler" together with async/await, trigger the action from a
different thread, or pass a callback to waitForResponse.
Function.prototype.toString returns a stub in plain Duktape¶
The Page/Frame/ElementHandle evaluate-family methods accept either source strings or
functions. Functions only carry source under "use transpiler" (or the -t CLI flag). In plain ES5 mode, fn.toString() returns
"function () { [ecmascript code] }" and the module throws an error indicating
that a source string is required.
Worker — Runtime only, no DOM¶
Worker.evaluate runs
in the worker’s global scope via Runtime.evaluate. Workers have no DOM, so $, $$, evaluateHandle and similar are
intentionally not exposed on Worker.
page.queryObjects requires a real JSHandle prototype¶
page.evaluateHandle("Foo.prototype") is passed in, not a class name string.
This matches Puppeteer.
ConsoleMessage shape¶
The console event
handler signature is (ConsoleMessage) — m.type(), m.text(), m.args(), m.location(). This matches Puppeteer.
Cleanup of launched Chrome on script exit¶
launch() registers a
Duktape finalizer on the returned Browser that fires on heap destruction — covering process.exit(), SIGTERM, SIGINT, parse errors, and
natural end-of-script. If browser.close() was not called before the script exited, Chrome is killed
and the temporary userDataDir is removed. This is best-effort: SIGKILL or segfault skips
finalizers, so a try/finally around browser.close() remains the most reliable shutdown.
Browsers obtained via connect() are intentionally left alone. The module did not launch Chrome
and does not assume ownership of the process.
HTML5 drag-and-drop requires explicit interception¶
page.mouse.dragAndDrop defaults to mouse-event click-and-drag (which works
for many “drag a slider thumb” UIs) but does not fire HTML5 dragstart/dragover/drop events. To exercise HTML5
drag-and-drop (file uploads via dropzones, sortable lists, kanban boards), page.setDragInterception(true)
must be called first. Subsequent calls to mouse.dragAndDrop (or
ElementHandle.dragAndDrop) then dispatch the HTML5 path.
Module-Level Functions¶
Once loaded, the rampart-chromeview module exposes these top-level entry points.
chrome.launch()¶
Launch a new Chrome child process and connect to it.
Usage:
var chrome = require("rampart-chromeview");
var browser = chrome.launch([options][, callback]);
Where:
-
optionsis an optional Object with the following properties:-
headless- a Boolean. Whentrue(the default), Chrome runs without a visible window. Passfalsefor a visible browser. -
executablePath- a String. The Chrome binary to launch. Auto-located from the candidate list under Prerequisites if omitted. -
userDataDir- a String. Chrome’s profile directory. If omitted, a fresh temporary directory is created and cleaned up automatically onbrowser.close(). -
args- an Array of Strings. Additional Chrome command-line flags.
-
-
callbackis an optional Function. If given, the call is non-blocking and the callback is invoked with(browser, error).
- Return Value:
- A Browser object.
Throws if Chrome fails to start within 15 seconds.
chrome.connect()¶
Attach to an already-running Chrome instance.
Usage:
/* By HTTP endpoint (the module fetches /json/version for the ws URL) */
var browser = chrome.connect({browserURL: "http://127.0.0.1:9222"});
/* Or by WebSocket URL directly */
var browser = chrome.connect({
browserWSEndpoint: "ws://127.0.0.1:9222/devtools/browser/abc-123"
});
Where options is one
of:
-
{browserURL: "http://host:port"}- The module fetches/json/versionfrom this URL to derive the WebSocket endpoint. -
{browserWSEndpoint: "ws://host:port/devtools/browser/..."}- Connect directly to the given WebSocket.
- Return Value:
-
A Browser object. The browser will
not be killed when
browser.close()is later called — only the WebSocket is disconnected.
chrome.executablePath()¶
Returns the absolute path to the Chrome/Chromium binary that launch() would use. Optionally
pass a String override to validate that a
custom path exists. Throws if nothing is found.
var path = chrome.executablePath();
var path = chrome.executablePath("/opt/chromium/chrome");
chrome.defaultArgs()¶
Returns the flag Array that launch() would pass to Chrome.
Intended for use in debugging and in wrapper functions that need to inspect, augment, or filter
the default flag set before launching.
var args = chrome.defaultArgs({headless: true});
chrome.devices¶
A registry of common device descriptors keyed by name, usable with page.emulate():
-
"iPhone SE","iPhone 12","iPhone 12 Pro","iPhone 13 Pro Max","iPhone 14" -
"iPad","iPad Pro" -
"Pixel 5","Pixel 7" -
"Galaxy S20","Galaxy S20 Ultra"
chrome.networkConditions¶
Preset network conditions keyed by name: "Slow 3G", "Fast 3G", "Slow 4G", "Fast 4G", "Offline". Pass either the
preset name or a custom Object to page.emulateNetworkConditions().
chrome.TimeoutError¶
A subclass of Error.
Thrown (or rejected) by waitForSelector, waitForFunction, waitForRequest, waitForResponse, and similar wait-family methods when their timeout
elapses. Use err
instanceof chrome.TimeoutError (or
err.name === "TimeoutError") to distinguish from other
failures.
Browser¶
Returned from chrome.launch() or chrome.connect(). Represents the whole Chrome process.
Every method that takes an optional [, cb] last argument supports the three calling
conventions described above (sync, callback, Promise).
-
browser.pages([cb])- Returns every open page across all browser contexts, including the initialabout:blankand any pages opened by chrome itself. Pages not already attached to are attached transparently and tracked as Page instances. -
browser.targets([cb])- Returns every CDP target as a Target (pages, service workers, browser-level targets, etc.). -
browser.version([cb]),browser.userAgent([cb])- Strings fromBrowser.getVersion.version()returns e.g."HeadlessChrome/147.0.0.0". -
browser.isConnected()- Returnstruewhile the worker thread is still alive. -
browser.disconnect([cb])- Closes the WebSocket and worker but leaves the Chrome process running. Appropriate forconnect()flows and when transferring control of the browser to another tool. -
browser.process()- For browsers started withlaunch(), returns{pid}for the Chrome child process. Returnsnullforconnect()-obtained browsers. -
browser.newPage([cb])- Open a new tab in the default browser context. Returns a Page. -
browser.createBrowserContext([cb])- Create an isolated (incognito-like) browser context with its own cookie jar and storage. Returns a BrowserContext. -
browser.defaultBrowserContext()- Return the default BrowserContext. Synchronous. -
browser.wsEndpoint()- Return the WebSocket URL of the current CDP connection. Synchronous. -
browser.close([cb])- Shut down the worker thread. If the browser was launched bylaunch(), also kill the Chrome process and remove the temporary user-data directory.
Browser events¶
browser.on(event,
handler), browser.off(event, handler), browser.once(event, handler) register/unregister
handlers for browser-level events. Subscribing to any of these events enables Target.setDiscoverTargets
automatically.
-
"targetcreated"- handler args:(Target) -
"targetdestroyed"- handler args:(Target) -
"targetchanged"- handler args:(Target) -
"disconnected"- handler args:(). Fired onbrowser.close(),disconnect(), or when rampart-net’s WebSocket ping/pong detects an ungracefully-exited Chrome (~30 seconds after the process dies).
BrowserContext¶
An isolated browser context — analogous to a profile, with its own cookie jar and storage.
Returned from browser.createBrowserContext() and browser.defaultBrowserContext().
-
context.pages([cb])- The pages belonging to this context. -
context.targets([cb])-browser.targets()filtered to this context. -
context.isIncognito()-truefor contexts created viacreateBrowserContext;falsefor the default context. -
context.newPage([cb])- Open a new tab inside this context. Cookies set here do not leak into the default context (or vice versa). -
context.close([cb])- Dispose the context (closes all its pages, deletes its cookies). A no-op on the default context. -
context.overridePermissions(origin, perms [, cb])- Pre-grant permissions tooriginso the page is not prompted.permsis an array of names from Chrome’s permission set:"geolocation","notifications","camera","microphone","clipboardReadWrite","backgroundSync", etc. -
context.clearPermissionOverrides([cb])- Reset any prior overrides.
Page¶
A single browser tab. Page carries the bulk of the rampart-chromeview API and is returned from
browser.newPage(),
context.newPage(),
browser.pages(), and
other entry points.
Content and Evaluation¶
-
page.evaluate(jsString [, cb])- Evaluate a JavaScript expression string in the page’s main execution context. If the expression evaluates to a Promise, it is awaited (via CDP’sawaitPromise: true). Thrown exceptions in the page become thrown Errors in Rampart.var n = page.evaluate("40 + 2"); // 42 var o = page.evaluate("({ok:true, v:document.title})"); var d = page.evaluate("fetch('/x').then(r=>r.json())"); // awaited
-
page.evaluateHandle(jsStringOrFn [, args...] [, cb])- Likeevaluate, but returns a JSHandle (or ElementHandle for DOM nodes) wrapping the in-page result instead of serializing it. This is the appropriate form for values that do not survive JSON serialization (window,Map, function objects, and similar). -
page.content([cb])- Returns the current page’s HTML (equivalent todocument.documentElement.outerHTML). -
page.setContent(html [, cb])- Replace the page’s current document withhtml. -
page.title([cb]),page.url([cb])- Returndocument.titleandlocation.hrefrespectively. -
page.evaluateOnNewDocument(fnOrSrc [, args...] [, cb])- Register a script (string or function) to run before any of the page’s own scripts on every subsequent document load (including iframes). Returns the CDP identifier string. -
page.addScriptTag(options [, cb])- Inject a<script>into the current document.optionsis one of{url},{path}, or{content}(and optionally{type}). -
page.addStyleTag(options [, cb])- Same shape asaddScriptTag, but injects a<link rel=stylesheet>(for{url}) or<style>(for{content}/{path}).
Selectors¶
-
page.$(selector [, cb])-document.querySelector, returning an ElementHandle ornull. -
page.$$(selector [, cb])-document.querySelectorAllas an Array of ElementHandles. -
page.$eval(selector, pageFnString [, args...] [, cb])- RunpageFnStringin the page with the matched element as the first argument. Throws if the selector matches nothing.var text = page.$eval("h1", "el => el.textContent"); var wide = page.$eval("#box", "(el, padding) => el.offsetWidth + padding", 10);
-
page.$$eval(selector, pageFnString [, args...] [, cb])- Same, butpageFnStringgets an array of all matching elements. -
page.$x(xpath [, cb])- XPath version of$$.
Interaction¶
-
page.click(selector [, options] [, cb])- Resolve the element, scroll it into view, and dispatch real mouse events at its bounding-box centre.options.buttonis"left"(default),"right", or"middle".options.clickCount(default1).options.delaymilliseconds between mousedown and mouseup.options.jsClickis a Boolean — whentrue, mouse dispatch is skipped andelement.click()is invoked via JavaScript instead. This alternative path is needed for elements with no bounding box (for example,display:none). -
page.focus(selector [, cb]),page.hover(selector [, cb])- Focus or hover the matched element. -
page.type(selector, text [, cb])- Focus the matched input/textarea and set its value. Dispatchesinputandchangeevents but not individual keydown events; for realistic typing, use Keyboardtype. -
page.select(selector, value1 [, value2, ...] [, cb])- Select option(s) in a<select>element byvalue. Firesinputandchangeevents. Returns an Array of the values that ended up selected.
Waiting¶
-
page.waitForSelector(selector [, options] [, cb])- Poll in-page untildocument.querySelector(selector)matches, or the timeout elapses.options.timeoutdefaults to30000ms (0waits indefinitely).options.pollingis the interval between checks (default50ms). -
page.waitForFunction(pageFnString [, options] [, cb])- PollpageFnStringin the page until it returns a truthy value. The truthy value is returned. -
page.waitForNavigation([options] [, cb])- Block until the nextload(ordomcontentloaded) event on this page. Install the waiter BEFORE triggering the navigation — there is a small race window otherwise:/* Transpiled mode — idiomatic */ const navP = page.waitForNavigation(); page.click("a.submit"); await navP;
-
page.waitForRequest(urlMatch [, options] [, cb])- Mirror ofwaitForResponseon the request side.urlMatchis a substring or a slash-wrapped regex source. -
page.waitForResponse(urlMatch [, options] [, cb])- Block until a network response arrives whose URL matches. Returns a Response. -
page.waitForXPath(xpath [, options] [, cb])- Poll untildocument.evaluate(xpath, ...)matches a node. -
page.waitForTimeout(ms [, cb])- Pause formsmilliseconds in-page. -
page.waitForFileChooser([options] [, cb])- Arm Chrome to intercept the next file chooser dialog (instead of showing a native picker) and resolve to a FileChooser:var chooserP = page.waitForFileChooser(); page.click("#upload-button"); // user gesture var chooser = await chooserP; chooser.accept(["/path/to/file.csv"]);
Screenshots and PDF¶
-
page.screenshot([options] [, cb])- Returns a PNG Buffer by default.options:-
type-"png"(default) or"jpeg" -
quality- JPEG quality, 0–100 -
fullPage- a Boolean; iftrue, capture the entire document -
clip-{x, y, width, height, scale} -
encoding-"base64"to return a string instead of a Buffer
-
-
page.pdf([options] [, cb])- Returns a PDF Buffer. Headless mode only.options(inches):format("letter"default,"legal","tabloid","a3","a4","a5"),landscape,printBackground,scale,paperWidth,paperHeight,marginTop,marginBottom,marginLeft,marginRight,preferCSSPageSize.
Cookies¶
-
page.setCookie(cookie [, cookie2, ...] [, cb])- Set one or more cookies. Each cookie is an Object:{name, value, domain, path, expires?, httpOnly?, secure?, sameSite?}. -
page.cookies([url1, url2, ...] [, cb])- Returns cookies. With no URLs, returns all cookies in the current context. Otherwise, returns cookies visible to the given URLs. -
page.deleteCookie(spec [, spec2, ...] [, cb])- Delete cookies matching each spec{name, url?, domain?, path?}.
Emulation¶
-
page.setViewport({width, height, deviceScaleFactor?, isMobile?, hasTouch?} [, cb])- Override device metrics. -
page.viewport()- Return the last viewport set, or query the live values from the page when nothing has been set. -
page.emulate()page.emulate(deviceOrName [, cb])- Apply both viewport and user-agent in one call. Accepts a device name (string) — looked up in chrome.devices — or a descriptor.page.emulate("iPhone 12"); page.goto("https://example.com/");
-
page.emulateMediaType(type [, cb])-typeis"screen","print", ornullto reset. Affects@mediaqueries, which is useful when rendering print stylesheets viapage.pdf. -
page.setGeolocation({latitude, longitude, accuracy?} [, cb])- Spoof the values returned bynavigator.geolocation.getCurrentPosition. Geolocation permission still needs to be granted to the origin. -
page.setUserAgent(userAgent [, cb])- Overridenavigator.userAgentand theUser-Agentheader on requests. -
page.setExtraHTTPHeaders(headers [, cb])-headersis{name: value, ...}. Applied to all subsequent requests initiated by this page. -
page.setJavaScriptEnabled(enabled [, cb])- Disable JavaScript execution for subsequent navigations. -
page.setDragInterception(enabled [, cb])- Whentrue, Chrome forwards drag operations to the host asInput.dragInterceptedevents instead of running its own drag UI. Prerequisite for HTML5 drag-and-drop viapage.mouse.drag/dragEnter/dragOver/drop. -
page.setBypassCSP(enabled [, cb])- Bypass any Content-Security-Policy headers the page returns. -
page.setCacheEnabled(enabled [, cb])- Toggle Chrome’s HTTP cache for this page. -
page.setOfflineMode(offline [, cb])- Simulate an offline network condition. -
page.emulateMediaFeatures(features [, cb])- Override@media (feature: value)queries.featuresis an array of{name, value}objects such as[{name: "prefers-color-scheme", value: "dark"}]. -
page.emulateTimezone(tz [, cb])- Override the page’s timezone.tzis an IANA name (e.g."America/Los_Angeles"). -
page.emulateCPUThrottling(rate [, cb])- Throttle CPU byrate(1= no throttling,2= 2x slower, etc.). -
page.emulateNetworkConditions(conditions [, cb])conditionsis either a preset name from chrome.networkConditions (e.g."Slow 3G") or an object{offline, latency, downloadThroughput, uploadThroughput}. Passnullto clear. -
page.emulateVisionDeficiency(type [, cb])-typeis one of"none","achromatopsia","blurredVision","deuteranopia","protanopia","tritanopia","reducedContrast".
Misc page accessors¶
-
page.metrics([cb])- Return a snapshot of Chrome’sPerformance.getMetricsas a flat{name: number}object. -
page.isClosed()- Returnstrueoncepage.close()has been called. -
page.bringToFront([cb])- Bring this page’s tab to the foreground. No-op in headless mode. -
page.target(),page.browser(),page.browserContext()- Back-references. -
page.workers()- The array of Worker instances attached to this page. ArmsTarget.setAutoAttachso future workers also appear. -
page.queryObjects(prototypeHandle [, cb])- Returns a JSHandle for the array of all live objects in the page whose prototype matches. -
page.setDefaultTimeout(ms)/page.setDefaultNavigationTimeout(ms)- Change the default timeout used bywaitForSelector,waitForFunction,waitForXPath, etc. Per-calloptions.timeoutstill wins. Returns the page for chaining. -
page.authenticate({username, password} [, cb])- Provide HTTP Basic/Digest credentials. Chrome re-runs any request that gets a 401/407 challenge using the supplied credentials. Passnullto clear.
Page events¶
page.on(event, handler), page.off(event, handler), page.once(event, handler), page.removeListener(event, handler).
| Event | Handler args |
|---|---|
"load"
|
()
|
"domcontentloaded"
|
()
|
"console"
|
(ConsoleMessage) - see ConsoleMessage
|
"pageerror"
|
(Error)
|
"dialog"
|
(Dialog) -
must call .accept() / .dismiss(); see Dialog
|
"framenavigated"
|
(frame) - raw
CDP frame object
|
"request"
|
(Request) -
requires setRequestInterception(true)
|
"response"
|
(Response)
|
"requestfailed"
|
(Request)
|
"requestfinished"
|
(Request)
|
"requestservedfromcache"
|
({requestId})
|
"close"
|
()
|
"popup"
|
(Page)
|
"frameattached"
|
({frameId,
parentFrameId?})
|
"framedetached"
|
({frameId,
reason?})
|
"error"
|
(payload) -
Inspector.targetCrashed
|
"workercreated"
|
(Worker)
|
"workerdestroyed"
|
(Worker)
|
Handler functions are invoked on the main thread’s event loop and will not fire during a sequence of blocking sync calls; see Event-driven features need the main event loop.
Binding functions¶
-
page.exposeFunction(name, fn [, cb])- Make the Rampart functionfncallable from page JavaScript aswindow.<name>(...args). The page-side call returns a Promise resolving tofn’s return value (or rejecting with a thrown error).page.exposeFunction("lookupUser", function(id) { return sql.one("SELECT * FROM users WHERE id=?", [id]); });
In the page:
const user = await window.lookupUser(42);
fnruns on the main thread’s event loop, soexposeFunctiononly works once the main script has yielded to the loop. -
page.removeExposedFunction(name [, cb])- Inverse ofexposeFunction.
Request interception¶
-
page.setRequestInterception(enabled [, cb])- Whentrue, subsequent requests pause and firepage.on("request", ...). The registered handler must callreq.continue(),req.abort(), orreq.respond()on every request — otherwise the page hangs.page.setRequestInterception(true); page.on("request", function(req) { if (/ads/.test(req.url())) { req.abort("BlockedByClient"); return; } req.continue(); });
Frame¶
Iframes (especially cross-origin ones) have their own JavaScript execution context and are
invisible to page.evaluate / page.$ in the main frame. Use page.frames() to enumerate and
reach inside them.
-
page.frames([cb])- Return an Array of Frame objects, starting with the main frame followed by its descendants (depth-first). -
page.mainFrame([cb])- Return the top-level Frame.
Frames have the same DOM-facing methods as Page, scoped to the frame’s execution context:
-
frame.evaluate(jsString [, cb]) -
frame.evaluateHandle(jsStringOrFn [, args...] [, cb]) -
frame.goto(url [, options] [, cb]),frame.setContent(html [, cb]),frame.waitForNavigation([options] [, cb]) -
frame.addScriptTag({url|path|content} [, cb]),frame.addStyleTag({url|path|content} [, cb]) -
frame.$(selector [, cb]),frame.$$(selector [, cb]),frame.$x(xpath [, cb]) -
frame.$eval(selector, pageFnString [, args...] [, cb]),frame.$$eval(selector, pageFnString [, args...] [, cb]) -
frame.click(selector [, cb]),frame.type(selector, text [, cb]),frame.focus(selector [, cb]),frame.hover(selector [, cb])- like thepage.*versions, but use synthetic DOM events (mouse coordinates would be relative to the iframe rather than the viewport). -
frame.select(selector, value1, ... [, cb]) -
frame.waitForSelector(selector [, options] [, cb]),frame.waitForFunction(pageFnString [, options] [, cb]),frame.waitForXPath(xpath [, options] [, cb]),frame.waitForTimeout(ms [, cb]) -
frame.waitForRequest(urlMatch [, options] [, cb]),frame.waitForResponse(urlMatch [, options] [, cb]) -
frame.content([cb]),frame.title([cb]) -
frame.url(),frame.name(),frame.frameId() -
frame.parentFrame(),frame.childFrames(),frame.isDetached() -
frame.page()
JSHandle¶
A handle to any in-page JavaScript value that cannot (or should not) be serialized over JSON.
Returned by page.evaluateHandle, frame.evaluateHandle, JSHandle.getProperty, etc.
-
handle.evaluate(fnSrc [, args...] [, cb])- RunfnSrcwith this handle bound as the first argument; serializes the return value. -
handle.evaluateHandle(fnSrc [, args...] [, cb])- Same, but returns another handle instead of serializing. -
handle.jsonValue([cb])- Best-effort serialization of the value. -
handle.getProperty(name [, cb])- Returns a JSHandle forobj[name]. -
handle.getProperties([cb])- Returns{name: JSHandle}of own keys. -
handle.asElement()- Returnsthisif it is an ElementHandle, elsenull. -
handle.dispose([cb])- Release the remote-object reference (idempotent).
ElementHandle is a subclass — every ElementHandle is a JSHandle, but only DOM nodes get the DOM-specific helpers below.
ElementHandle¶
Returned from page.$(), page.$$(), page.$x(), frame.$(), frame.$$(), and from evaluateHandle when the value is a DOM node. Backed by a remote-object
reference in the page; the reference is released by .dispose().
-
handle.evaluate(fnSrcString [, args...] [, cb])- Runs withthis(and first argument) bound to the element. -
handle.textContent([cb]),handle.getAttribute(name [, cb]) -
handle.boundingBox([cb])- Returns{x, y, width, height}ornull. -
handle.screenshot([options] [, cb])- Page screenshot clipped to this element’s bounding box. -
handle.click([cb]),handle.focus([cb]),handle.hover([cb]) -
handle.type(text [, cb])- Sets value and firesinput/change. -
handle.press(key [, opts] [, cb])- Focus the element, then dispatch a real key press. -
handle.tap([cb])- Touch-emulated tap viaInput.dispatchTouchEvent. -
handle.select(value1 [, value2, ...] [, cb])- Set a<select>to the listed option values; returns the array of values that ended up selected. -
handle.uploadFile(path1 [, path2, ...] [, cb])- Set the file list on an<input type=file>element. -
handle.contentFrame([cb])- For an<iframe>/<frame>element, returns the Frame inside it, ornull. -
handle.scrollIntoView([cb])- Scroll the element into the middle of the viewport. -
handle.isVisible([cb])/handle.isHidden([cb])- Display and size check. -
handle.isIntersectingViewport([opts] [, cb])-trueif any part is in the viewport (opts.threshold0–1). -
handle.$(sel [, cb]),handle.$$(sel [, cb]),handle.$x(xp [, cb])- Scoped descendants. -
handle.$eval(sel, fnStr [, args...] [, cb]),handle.$$eval(sel, fnStr [, args...] [, cb]) -
handle.drag(targetHandleOrPoint [, cb])- Start an HTML5 drag from this element’s centre; returns the capturedDragData. -
handle.dragEnter(data [, cb]),handle.dragOver(data [, cb]),handle.drop(data [, cb])- Dispatch the HTML5 drag event at this element’s centre. -
handle.dragAndDrop(targetHandleOrPoint [, opts] [, cb])- Full HTML5 drag-and-drop from this element to the target. -
handle.dispose([cb])- Release the remote-object reference.
var h = page.$("#username");
h.type("alice");
printf("value: %s\n", h.evaluate("el => el.value"));
h.dispose();
/* Screenshot a specific card */
var card = page.$(".product-card");
fprintf("/tmp/card.png", "%s", card.screenshot());
card.dispose();
Mouse¶
Accessed as page.mouse. Dispatches real CDP Input.dispatchMouseEvents.
-
page.mouse.move(x, y [, opts] [, cb]) -
page.mouse.down([opts] [, cb])-{button, clickCount} -
page.mouse.up([opts] [, cb]) -
page.mouse.click(x, y [, opts] [, cb])-{button, clickCount, delay}. Move, press, wait, release. -
page.mouse.wheel([opts] [, cb])-{deltaX, deltaY} -
page.mouse.dragAndDrop({x, y}, {x, y} [, opts] [, cb])- Drag-and-drop. Whenpage.setDragInterception(true)has been called, dispatches HTML5 drag events; otherwise falls back toclickAndDrag.opts:{steps, delay}. -
page.mouse.clickAndDrag({x, y}, {x, y} [, opts] [, cb])- Mouse-event click-and-drag for UIs that do not use HTML5 drag. -
page.mouse.drag({x, y}, {x, y} [, cb])- Start an HTML5 drag and return the capturedDragData. Requirespage.setDragInterception(true). -
page.mouse.dragEnter({x, y}, data [, cb]),page.mouse.dragOver({x, y}, data [, cb]),page.mouse.drop({x, y}, data [, cb]) -
page.mouse.reset([cb])- Release any pressed buttons.
Keyboard¶
Accessed as page.keyboard. Dispatches real CDP Input.dispatchKeyEvents.
-
page.keyboard.down(key [, cb]),page.keyboard.up(key [, cb]) -
page.keyboard.press(key [, opts] [, cb])-opts.delay(ms) between down and up. -
page.keyboard.type(text [, opts] [, cb])- Fast by default (usesInput.insertText). Pass{delay: ms}to interleave a per-character delay. -
page.keyboard.sendCharacter(char [, cb]) -
page.keyboard.reset([cb])- Release any held modifiers.
key is either a single
character ("a") or a
named key: "Enter",
"Tab", "Backspace", "Delete", "Escape", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End", "PageUp", "PageDown", "Space", "Shift", "Control", "Alt", "Meta".
Request¶
Passed to page.on("request",
handler) when interception is on, and to page.on("requestfailed"|"requestfinished", handler)
for any request (no interception needed).
-
req.url(),req.method(),req.headers(),req.postData(),req.resourceType() -
req.frame()- The Frame that initiated this request. -
req.response()- The matching Response, onceNetwork.responseReceivedhas fired (otherwisenull). -
req.failure()-{errorText}if the request failed, elsenull. -
req.redirectChain()- Array of Requests representing the redirect steps that led here. -
req.isNavigationRequest()-truefor top-levelDocumentrequests. -
req.initiator()- The rawNetwork.Initiator. -
req.continue([overrides] [, cb])- Let the request proceed, with optional{url, method, headers, postData}overrides. -
req.abort([reason] [, cb])- e.g."BlockedByClient","Failed","ConnectionClosed","AccessDenied","InternetDisconnected". -
req.respond({status, headers, body} [, cb])- Fulfill from Rampart without hitting the network.bodycan be a String or Buffer.
Response¶
Passed to page.on("response",
handler) and returned from page.waitForResponse(urlMatch,
...).
-
resp.url(),resp.status(),resp.statusText(),resp.headers() -
resp.mimeType() -
resp.ok()-truefor status 2xx (or 0 fordata:). -
resp.request()- The originating Request. -
resp.frame()- The Frame that initiated the matching request. -
resp.fromCache()-trueif served from disk/prefetch cache. -
resp.fromServiceWorker()-trueif served by a SW. -
resp.remoteAddress()-{ip, port}of the server, ornull. -
resp.timing()- RawNetwork.ResourceTiming(ornull). -
resp.securityDetails()- RawSecurityDetails(ornull). -
resp.text([cb])- Body decoded to a String. -
resp.buffer([cb])- Body as a raw Buffer. -
resp.json([cb])-JSON.parse(resp.text()).
Target¶
A CDP target: a page, service worker, browser-level target, etc.
-
target.targetId(),target.type(),target.url() -
target.browser(),target.browserContext() -
target.page()- Returns the Page instance if this is a page-type target and the correspondingPageis already tracked; otherwisenull. -
target.createCDPSession([cb])- Attach (if needed) and return a CDPSession for raw CDP commands against this target.
CDPSession¶
The raw escape hatch for any CDP command not covered by the higher-level wrappers. Returned from
target.createCDPSession() (or use the page’s existing session via
page.target().createCDPSession()).
-
session.send(method [, params] [, cb])- Issue any CDP command. -
session.on(method, handler)- Subscribe to any CDP event; handler receives the raw params object. -
session.off(method [, handler])- Remove a listener (or all listeners formethodifhandleris omitted). -
session.detach([cb])- Close the session. -
session.id()- The underlying CDPsessionId.
var session = page.target().createCDPSession();
session.send("Animation.enable");
session.on("Animation.animationStarted", function(p) {
printf("animation started: %s\n", p.animation.id);
});
Worker¶
Returned from page.workers() and the workercreated event.
-
worker.url(),worker.type(),worker.targetId() -
worker.evaluate(jsStringOrFn [, args...] [, cb])- Run code in the worker’s global scope.
Coverage¶
Accessed as page.coverage.
-
page.coverage.startJSCoverage([opts] [, cb])-opts.reportAnonymousScripts: trueincludes anonymous scripts. -
page.coverage.stopJSCoverage([cb])- Returns the array ofProfiler.ScriptCoverageobjects fromProfiler.takePreciseCoverage. -
page.coverage.startCSSCoverage([cb]),page.coverage.stopCSSCoverage([cb])- ReturnsCSS.RuleUsage[]describing which rules ran.
Tracing¶
Accessed as page.tracing.
-
page.tracing.start([opts] [, cb])-opts.categories(string array) andopts.screenshots(bool). -
page.tracing.stop([cb])- Returns a Buffer containing a JSON array of trace events. Passopts.pathtostart()to also write to disk alongside the returned buffer.
FileChooser¶
Returned from page.waitForFileChooser(). Resolve it before the user gesture’s task
finishes, or Chrome will time the picker out.
-
chooser.isMultiple()-trueif the input hasmultiple. -
chooser.accept(paths [, cb])- Set the file list;pathsis a String or an Array of strings. Files must exist on the Rampart host. -
chooser.cancel([cb])- Dismiss without selecting files.
Accessibility¶
Accessed as page.accessibility.
-
page.accessibility.snapshot([opts] [, cb])- Return a tree of{role, name, value?, children?}mirroring the page’s accessibility tree.
var snap = page.accessibility.snapshot();
(function walk(n, d) {
printf("%s%s%s\n", " ".repeat(d), n.role,
n.name ? ' "' + n.name + '"' : "");
(n.children || []).forEach(function(c) { walk(c, d + 1); });
})(snap, 0);
ConsoleMessage¶
Passed to page.on("console",
handler).
-
m.type()-"log","warning","error","info","debug", … -
m.text()- Arguments joined into a single string. -
m.args()- The raw CDPRemoteObjects passed to the console call. -
m.location()-{url, lineNumber, columnNumber}of the call site (best-effort).
Dialog¶
Passed to page.on("dialog",
handler) when the page invokes alert(), confirm(), prompt(), or a beforeunload. The handler
must call ``.accept()`` or ``.dismiss()`` or the page blocks forever — confirm() and prompt() in particular do not
return until the dialog is resolved.
-
dialog.type()-"alert"/"confirm"/"prompt"/"beforeunload". -
dialog.message()- The text passed to the JS call. -
dialog.defaultValue()- The default prompt response (prompt only). -
dialog.accept([promptText] [, cb])- Accept; forprompt, pass the text to return to the page. -
dialog.dismiss([cb])- Cancel.
page.on("dialog", async function(d) {
if (d.type() === "confirm") await d.accept();
else await d.dismiss();
});
Example: scrape a rendered SPA and save a PDF¶
"use transpiler"
rampart.globalize(rampart.utils);
const chrome = require("rampart-chromeview");
async function main() {
const browser = await chrome.launch({headless: true});
const page = await browser.newPage();
/* Block analytics and image requests to speed things up. */
await page.setRequestInterception(true);
page.on("request", function(req) {
const url = req.url();
if (/google-analytics|doubleclick/.test(url)) return req.abort();
if (req.resourceType() === "image") return req.abort();
req.continue();
});
/* Pretend to be a desktop browser. */
await page.setViewport({width: 1280, height: 800});
await page.setUserAgent("Mozilla/5.0 rampart-chromeview");
/* Load the page and wait for the target element. */
await page.goto("https://example.com/dashboard", {waitUntil: "load"});
await page.waitForSelector("#report-root", {timeout: 10000});
/* Pull structured data out of the rendered DOM. */
const rows = await page.$$eval("#report-root tr",
"els => els.map(tr => Array.from(tr.children).map(td => td.textContent.trim()))");
printf("got %d rows\n", rows.length);
/* Render the same view to a PDF. */
const pdf = await page.pdf({format: "letter", printBackground: true});
fprintf("/tmp/report.pdf", "%s", pdf);
printf("wrote /tmp/report.pdf (%d bytes)\n", pdf.length);
await browser.close();
}
main().catch(function(e) { printf("error: %s\n", e && e.stack || e); });
Using puppeteer-extras¶
Rampart ships an accompanying single-file module, puppeteer-extras, that
packages puppeteer-extra@3.3.6 and puppeteer-extra-plugin-stealth@2.11.2
together with their entire transitive npm closure (37 packages, 141 internal modules) into a
single require()-able
file that resolves its own internal dependencies. The module is intended to be used in
conjunction with rampart-chromeview, which serves as the underlying launcher.
What it provides¶
-
addExtra(launcher)- Wraps a Puppeteer-compatible launcher (rampart-chromeviewitself) and returns aPuppeteerExtrainstance that supports thepuppeteer-extraplugin pipeline. -
StealthPlugin()- The stockpuppeteer-extra-plugin-stealthfactory. Returns a plugin with 16 stock evasions (navigator.webdriver,navigator.languages,navigator.plugins,chrome.runtime, WebGL vendor/renderer spoofing, user-agent normalization, etc.). -
PuppeteerExtra- The constructor, exposed for advanced usage.
Loading¶
var pe = require("puppeteer-extras");
The bundle is self-contained — no node_modules/ lookup required at runtime. License attribution for all 37
packaged npm packages (each with its original LICENSE text, version, author, and license type
from package.json)
is included as a comment block at the top of the file.
Use with rampart-chromeview¶
The integration pattern matches the one used by npm scripts built on puppeteer: addExtra wraps a launcher,
use registers a
plugin, and launch
then starts a browser. rampart-chromeview itself is the launcher passed to addExtra:
"use transpiler"
var rch = require("rampart-chromeview");
var pe = require("puppeteer-extras");
var puppeteer = pe.addExtra(rch);
puppeteer.use(pe.StealthPlugin());
async function main() {
var browser = await puppeteer.launch({headless: true});
var page = await browser.newPage();
await page.goto("about:blank");
/* The stealth plugin's evasions have already been injected
via evaluateOnNewDocument before any page script runs. */
var webdriver = await page.evaluate("navigator.webdriver");
var ua = await page.evaluate("navigator.userAgent");
rampart.utils.printf("webdriver: %J\n", webdriver);
rampart.utils.printf("userAgent: %s\n", ua);
await browser.close();
}
main().catch(function(e) { rampart.utils.printf("FAIL: %s\n", e); });
A run with stealth active produces output of the form:
webdriver: false
userAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36
navigator.webdriver
no longer returns true, and the HeadlessChrome token is absent from the user-agent — the two standard
fingerprint markers vanilla-headless Chrome exposes are both suppressed by the stealth plugin’s
evasions.
Why “use transpiler” is required¶
The stealth evasions are JavaScript functions injected into the running page via page.evaluateOnNewDocument(fn).
Chrome executes them in its own JavaScript engine, not in Rampart’s, so the original
ES2015+ source text of each evasion is required (the bundled transpiled body contains
_TrN_Sp.* helper
references that Chrome does not have). The transpiler stamps each function with its original
source via the _TrN_Sp._fs(fn, "src") mechanism;
rampart-chromeview’s internal _requireSource routine reads fn.__source__ and forwards that
source string to Chrome.
Scripts that use puppeteer-extras must therefore be run with the transpiler active, supplied
either as "use transpiler" at the top of the file or as the -t command-line flag.
Bundled packages¶
The 37 npm packages baked into the bundle (each retains its original license, all permissive — MIT or ISC):
arr-union,
balanced-match,
brace-expansion,
clone-deep,
concat-map,
debug, deepmerge, for-in, for-own, fs-extra, fs.realpath, glob, graceful-fs, inflight, inherits, is-buffer, is-extendable, is-plain-object, isobject, jsonfile, kind-of, lazy-cache, merge-deep, minimatch, mixin-object, ms, once, path-is-absolute, puppeteer-extra, puppeteer-extra-plugin,
puppeteer-extra-plugin-stealth, puppeteer-extra-plugin-user-data-dir, puppeteer-extra-plugin-user-preferences, rimraf, shallow-clone, universalify, wrappy.
License: each package retains its original copyright and license. The bundle’s header inlines
every package’s LICENSE file verbatim.
Why a bundle¶
The rationale for shipping a pre-built single-file bundle instead of relying on a separate
npm install puppeteer-extra step:
- No
node_modulestree is required. The single file is placed on the rampart module search path and resolved via the standardrequire()mechanism. - The installed version is reproducible: every install resolves to the same vendored set of package versions, eliminating the risk of a transitive-dependency change silently altering stealth behaviour.
- License attribution is aggregated. Every bundled package’s
LICENSEtext is inlined in the bundle header, satisfying distribution requirements without a separatelicenses/directory. - The transitive closure is contained in a single file, which simplifies source review and auditing.
Scripts that prefer the upstream npm packages directly are still supported: a node_modules tree placed next to
the script allows require("puppeteer-extra") and require("puppeteer-extra-plugin-stealth") to resolve via the standard
module-lookup path. The bundle is an alternative deliverable, not a replacement.