Executes page JavaScript and WebAssembly with full access to BrowserOxide's DOM and Web APIs.
| Property | Value |
|---|---|
| Engine | V8 (same engine as Chrome, Deno, Node.js) |
| Rust bindings | rusty_v8 (MIT) — stable since v129.0.0 |
| Ops layer | deno_core (MIT) — Rust functions as JS ops, event loop, module loading |
| ES conformance | 100% (ES2024+, all proposals Chrome supports) |
| WebAssembly | Full support (free with V8) — WASM MVP, SIMD, threads, GC |
| Build | Prebuilt V8 static libraries (seconds). Source build: ~30min |
| Binary size | ~30MB (V8 static lib) |
QuickJS (previous plan) fails for SOTA 2026:
| Requirement | QuickJS | V8 |
|---|---|---|
| Cloudflare Turnstile WASM challenges | No WASM support | Full WASM |
| Kasada proof-of-work (WASM) | Fails | Passes |
| Heavy SPA bundles (React 19, Next.js 15) | 70x slower, pages timeout | JIT-compiled, native speed |
| ES2024+ features | ~ES2023 only | Complete |
| Performance.now() timing accuracy | Different from Chrome | Same as Chrome |
deno_core adds critical infrastructure on top of raw V8 bindings:
- Op system: Define Rust functions callable from JS with
#[op2]macro. Handles serialization, async, error propagation. - Event loop: Integrated tokio event loop that drives V8 microtasks, timers, and async ops.
- Module loader: ES module resolution and loading via a
ModuleLoadertrait. - Snapshot support: Serialize initialized V8 heap → restore instantly for fast page creation.
- Extension system: Bundle ops + JS glue into reusable extensions.
deno_core does NOT include Deno's runtime (no built-in fetch, no file system, no Node compat). It's a blank V8 + ops infrastructure.
js_runtime/
├── src/
│ ├── lib.rs # JsRuntime struct — owns V8 Isolate via deno_core
│ ├── runtime.rs # Runtime creation, snapshot, extension loading
│ ├── extensions/ # deno_core extensions (groups of ops)
│ │ ├── dom_ext.rs # DOM ops (querySelector, createElement, etc.)
│ │ ├── window_ext.rs # Window global (setTimeout, location, navigator)
│ │ ├── fetch_ext.rs # fetch() → net crate
│ │ ├── console_ext.rs # console.log/warn/error → Rust tracing
│ │ ├── event_ext.rs # addEventListener, dispatchEvent
│ │ ├── storage_ext.rs # localStorage, sessionStorage
│ │ ├── canvas_ext.rs # Canvas 2D API → canvas crate
│ │ ├── webgl_ext.rs # WebGL parameter stubs
│ │ ├── crypto_ext.rs # crypto.getRandomValues, crypto.subtle
│ │ ├── url_ext.rs # URL, URLSearchParams
│ │ ├── timer_ext.rs # setTimeout, setInterval → event_loop
│ │ ├── perf_ext.rs # performance.now(), performance.timing
│ │ ├── intl_ext.rs # Intl.* locale verification (V8 built-in)
│ │ ├── speech_ext.rs # speechSynthesis.getVoices() stub
│ │ ├── media_ext.rs # MediaDevices, MediaSession stubs
│ │ └── websocket_ext.rs # WebSocket client → tokio-tungstenite
│ ├── bindings/
│ │ ├── mod.rs
│ │ ├── document.rs # Document interface → DOM ops
│ │ ├── element.rs # Element/HTMLElement → DOM ops
│ │ ├── node.rs # Node interface → DOM ops
│ │ ├── navigator.rs # navigator.* (stealth-controlled)
│ │ ├── screen.rs # screen.* (stealth-controlled)
│ │ ├── location.rs # window.location
│ │ ├── history.rs # history.pushState, replaceState
│ │ └── mutation_observer.rs
│ ├── module_loader.rs # ES module resolution → net crate fetch
│ └── snapshot.rs # V8 heap snapshot for fast startup
├── tests/
│ ├── dom_manipulation.rs
│ ├── wasm_execution.rs # WASM module instantiation
│ ├── fetch_tests.rs
│ └── timer_tests.rs
└── Cargo.toml
1. Create V8 Isolate (or restore from snapshot)
2. Create Context with global object template
3. Populate globals (window, document, navigator, ...)
4. Execute page scripts
5. Run event loop (timers, fetch callbacks, WASM)
6. Extract content
7. Destroy Isolate (all JS objects freed)
Snapshots — Pre-initialize DOM bindings + Web API stubs, serialize V8 heap. On page creation, restore from snapshot (~5ms) instead of re-running setup JS (~50ms). This is how Deno achieves fast startup.
Concurrent pages — Each page gets its own V8 Isolate. Isolates are independent and can run on different tokio tasks. No shared mutable state between pages.
The JS global must match what anti-bot scripts expect from Chrome. The full surface includes 50+ navigator properties, window properties, and API constructors. See STEALTH.md for the exhaustive list.
Critical globals that must exist and behave correctly:
// Identity
window.chrome // Must exist with correct shape
navigator.userAgent // Profile UA
navigator.webdriver // undefined (NOT false)
navigator.plugins // Realistic plugin list
navigator.languages // Profile languages
// APIs anti-bot probes for existence
window.speechSynthesis // Must have getVoices()
window.Notification // Constructor + permission
window.RTCPeerConnection // WebRTC presence
navigator.mediaDevices // enumerateDevices()
navigator.bluetooth // Presence (Chrome)
navigator.usb // Presence (Chrome)
navigator.credentials // Credential Management
navigator.permissions // query() with realistic states
// Timing
performance.now() // Chrome resolution (100μs normal, 5μs COOP)
performance.memory // Chrome-specific heap info
// Integrity checks
document.hasFocus() // Must return true
Function.prototype.toString.call(fn) // Must return "[native code]" for native fnsV8 provides WASM for free. Critical for:
- Cloudflare Turnstile: Runs WASM proof-of-work (~50-200ms computation)
- Kasada: Polymorphic WASM challenges with environment integrity checks
- DataDome: WASM-based browser verification (2025+)
Implementation: WASM modules loaded via WebAssembly.compile() / WebAssembly.instantiate() run natively in V8. No additional work needed beyond exposing the standard WebAssembly global (which V8 provides by default).
Key consideration: WASM timing must be realistic. Running in a debugger or instrumented V8 adds overhead that Kasada detects. BrowserOxide runs V8 without debugging hooks by default.
ES modules (import ... from "...") resolved via custom ModuleLoader:
- Relative URLs resolved against page's base URL
- Absolute URLs fetched via net crate
- Module source cached (same-URL dedup)
import.meta.urlreturns resolved module URL- Dynamic
import()supported (returns Promise) - WASM modules importable via
import ... from "mod.wasm"(stage 3 proposal)