browser_oxide 0.1.3

Stealth headless browser engine in Rust: real HTML/CSS/DOM/JS, V8 via deno_core, own BoringSSL TLS/JA4 fingerprint, no Chromium, no CDP — for anti-bot web scraping, archival, and AI agents
Documentation
//! Audio compressor parity vs captured Chrome 147 ground truth.
//!
//! Real Chrome 147 (macOS arm64, captured 2026-04-28):
//!   sum of |buf[4500..5000]| === 124.04348155876505
//!
//! Open: this test uses CreepJS-style overrides (threshold=-50,
//! attack=0). At BLINK_OSCILLATOR_SCALE=0.47624 (calibrated for
//! Chrome *default* params), we get ~103.92 — a 16% gap. Empirical
//! scanning shows no single scale matches both default and -50
//! threshold, indicating a bug in the compressor port's response to
//! threshold (not the oscillator). Fixing requires bisecting Blink's
//! static-compression-curve / makeup-gain math (~1 wk; deferred).
//! Test asserts the looser cluster band; companion test
//! audio_fingerprint.rs locks the tight (~3.6 ppm) parity for the
//! default-params scenario that real fingerprint scripts use.

use browser_oxide::Page;

#[tokio::test]
async fn audio_compressor_in_blink_range() {
    let mut page = Page::from_html(
        "<!DOCTYPE html><html><body></body></html>",
        None::<browser_oxide::stealth::StealthProfile>,
    )
    .await
    .unwrap();
    // Stash the rendered buffer on globalThis on first eval, read sum on second.
    page.evaluate(
        r#"
        const oac = new OfflineAudioContext(1, 5000, 44100);
        const osc = oac.createOscillator();
        osc.type = 'triangle'; osc.frequency.value = 10000;
        const comp = oac.createDynamicsCompressor();
        comp.threshold.value = -50; comp.knee.value = 40; comp.ratio.value = 12;
        comp.attack.value = 0; comp.release.value = 0.25;
        osc.connect(comp); comp.connect(oac.destination); osc.start(0);
        oac.startRendering().then(buf => {
            const data = buf.getChannelData(0);
            let sum = 0;
            for (let i = 4500; i < 5000; i++) sum += Math.abs(data[i]);
            globalThis.__audioSum = sum;
        });
        'rendered';
        "#,
    )
    .unwrap();
    // Drive the event loop so the async OfflineAudioContext render completes and
    // its `.then()` (which sets __audioSum) runs. A bare microtask drain isn't
    // enough under deno_core 0.403 — startRendering resolves via the event loop.
    let _ = page
        .event_loop()
        .run_until_idle(std::time::Duration::from_secs(3))
        .await;
    let r = page
        .evaluate("String(globalThis.__audioSum)")
        .unwrap_or_default();
    eprintln!("Engine audio compressor sum: {r}");
    eprintln!("Chrome 147 captured sum:    124.04348155876505");
    let parsed: f64 = r.parse().unwrap_or(0.0);
    assert!(parsed > 0.0, "audio sum must be > 0: {r}");

    let chrome_sum: f64 = 124.04348155876505;
    let delta = (parsed - chrome_sum).abs();
    eprintln!("Delta vs Chrome 147: {delta:.6e}");
    // Cluster band — the threshold=-50 scenario lands ~16% off Chrome's
    // value with BLINK_OSCILLATOR_SCALE calibrated for default params.
    // CreepJS KnownAudio accepts cluster matches; tighter parity
    // requires the compressor-port bug fix tracked in audio.rs.
    assert!(
        parsed > 100.0 && parsed < 200.0,
        "audio sum must be in Blink cluster (100..200): got {parsed}"
    );
}