camoufox
Version:
JavaScript port of Camoufox - a tool for Firefox anti-fingerprinting and browser automation.
105 lines (104 loc) • 3.31 kB
JavaScript
import { CannotExecuteXvfb, CannotFindXvfb, VirtualDisplayNotSupported, } from './exceptions.js';
import { OS_NAME } from './pkgman.js';
import { execFileSync, spawn } from 'child_process';
import { existsSync } from 'fs';
// import { join } from 'path';
import { tmpdir } from 'os';
// import { globSync } from 'glob';
import { randomInt } from 'crypto';
// import { Lock } from 'async-mutex';
export class VirtualDisplay {
debug;
proc = null;
_display = null;
// private _lock = new Lock();
constructor(debug = false) {
this.debug = debug;
}
get xvfb_args() {
return [
"-screen", "0", "1x1x24",
"-ac",
"-nolisten", "tcp",
"-extension", "RENDER",
"+extension", "GLX",
"-extension", "COMPOSITE",
"-extension", "XVideo",
"-extension", "XVideo-MotionCompensation",
"-extension", "XINERAMA",
"-shmem",
"-fp", "built-ins",
"-nocursor",
"-br",
];
}
get xvfb_path() {
const path = execFileSync('which', ['Xvfb']).toString().trim();
if (!path) {
throw new CannotFindXvfb("Please install Xvfb to use headless mode.");
}
if (!existsSync(path) || !execFileSync('test', ['-x', path])) {
throw new CannotExecuteXvfb(`I do not have permission to execute Xvfb: ${path}`);
}
return path;
}
get xvfb_cmd() {
return [this.xvfb_path, `:${this.display}`, ...this.xvfb_args];
}
execute_xvfb() {
if (this.debug) {
console.log('Starting virtual display:', this.xvfb_cmd.join(' '));
}
this.proc = spawn(this.xvfb_cmd[0], this.xvfb_cmd.slice(1), {
stdio: this.debug ? 'inherit' : 'ignore',
detached: true,
});
}
get() {
VirtualDisplay.assert_linux();
// this._lock.runExclusive(() => {
if (!this.proc) {
this.execute_xvfb();
}
else if (this.debug) {
console.log(`Using virtual display: ${this.display}`);
}
// });
return `:${this.display}`;
}
kill() {
// this._lock.runExclusive(() => {
if (this.proc && !this.proc.killed) {
if (this.debug) {
console.log('Terminating virtual display:', this.display);
}
this.proc.kill();
}
// });
}
static _get_lock_files() {
const tmpd = process.env.TMPDIR || tmpdir();
try {
return [];
// return globSync(join(tmpd, ".X*-lock")).filter(p => existsSync(p));
}
catch {
return [];
}
}
static _free_display() {
const ls = VirtualDisplay._get_lock_files().map(x => parseInt(x.split("X")[1].split("-")[0]));
return ls.length ? Math.max(99, Math.max(...ls) + randomInt(3, 20)) : 99;
}
get display() {
if (this._display === null) {
this._display = VirtualDisplay._free_display();
}
return this._display;
}
static assert_linux() {
if (OS_NAME !== 'lin') {
throw new VirtualDisplayNotSupported("Virtual display is only supported on Linux.");
}
}
}