@hieutran094/camoufox-js 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,27 @@
1
+ import { firefox } from 'playwright-core';
2
+ import { launchOptions, syncAttachVD } from './utils.js';
3
+ import { VirtualDisplay } from './virtdisplay.js';
4
+ export async function Camoufox(launch_options = {}) {
5
+ const { headless, user_data_dir, ...launchOptions } = launch_options;
6
+ return NewBrowser(firefox, headless, {}, user_data_dir ?? false, false, launchOptions);
7
+ }
8
+ export async function NewBrowser(playwright, headless = false, fromOptions = {}, userDataDir = false, debug = false, launch_options = {}) {
9
+ let virtualDisplay = null;
10
+ if (headless === 'virtual') {
11
+ virtualDisplay = new VirtualDisplay(debug);
12
+ launch_options['virtual_display'] = virtualDisplay.get();
13
+ launch_options.headless = false;
14
+ }
15
+ else {
16
+ launch_options.headless ||= headless;
17
+ }
18
+ if (!fromOptions || Object.keys(fromOptions).length === 0) {
19
+ fromOptions = await launchOptions({ debug, ...launch_options });
20
+ }
21
+ if (typeof userDataDir === 'string') {
22
+ const context = await playwright.launchPersistentContext(userDataDir, fromOptions);
23
+ return syncAttachVD(context, virtualDisplay);
24
+ }
25
+ const browser = await playwright.launch(fromOptions);
26
+ return syncAttachVD(browser, virtualDisplay);
27
+ }
@@ -0,0 +1,89 @@
1
+ import { DefaultAddons } from './addons.js';
2
+ import { SUPPORTED_OS } from './fingerprints.js';
3
+ import { VirtualDisplay } from './virtdisplay.js';
4
+ import { PathLike } from 'fs';
5
+ import { Fingerprint, FingerprintGeneratorOptions } from 'fingerprint-generator';
6
+ import { LaunchOptions as PlaywrightLaunchOptions } from 'playwright-core';
7
+ type Screen = FingerprintGeneratorOptions['screen'];
8
+ export declare function getAsBooleanFromENV(name: string, defaultValue?: boolean | undefined): boolean;
9
+ export declare function syncAttachVD(browser: any, virtualDisplay?: VirtualDisplay | null): any;
10
+ export interface LaunchOptions {
11
+ /** Operating system to use for the fingerprint generation.
12
+ * Can be "windows", "macos", "linux", or a list to randomly choose from.
13
+ * Default: ["windows", "macos", "linux"]
14
+ */
15
+ os?: typeof SUPPORTED_OS[number] | (typeof SUPPORTED_OS[number])[];
16
+ /** Whether to block all images. */
17
+ block_images?: boolean;
18
+ /** Whether to block WebRTC entirely. */
19
+ block_webrtc?: boolean;
20
+ /** Whether to block WebGL. To prevent leaks, only use this for special cases. */
21
+ block_webgl?: boolean;
22
+ /** Disables the Cross-Origin-Opener-Policy, allowing elements in cross-origin iframes to be clicked. */
23
+ disable_coop?: boolean;
24
+ /** Calculate longitude, latitude, timezone, country, & locale based on the IP address.
25
+ * Pass the target IP address to use, or `true` to find the IP address automatically.
26
+ */
27
+ geoip?: string | boolean;
28
+ /** Humanize the cursor movement.
29
+ * Takes either `true`, or the MAX duration in seconds of the cursor movement.
30
+ * The cursor typically takes up to 1.5 seconds to move across the window.
31
+ */
32
+ humanize?: boolean | number;
33
+ /** Locale(s) to use. The first listed locale will be used for the Intl API. */
34
+ locale?: string | string[];
35
+ /** List of Firefox addons to use. */
36
+ addons?: string[];
37
+ /** Fonts to load into the browser (in addition to the default fonts for the target `os`).
38
+ * Takes a list of font family names that are installed on the system.
39
+ */
40
+ fonts?: string[];
41
+ /** If enabled, OS-specific system fonts will not be passed to the browser. */
42
+ custom_fonts_only?: boolean;
43
+ /** Default addons to exclude. Passed as a list of `DefaultAddons` enums. */
44
+ exclude_addons?: (keyof typeof DefaultAddons)[];
45
+ /** Constrains the screen dimensions of the generated fingerprint. */
46
+ screen?: Screen;
47
+ /** Set a fixed window size instead of generating a random one. */
48
+ window?: [number, number];
49
+ /** Use a custom BrowserForge fingerprint. If not provided, a random fingerprint will be generated
50
+ * based on the provided `os` & `screen` constraints.
51
+ */
52
+ fingerprint?: Fingerprint;
53
+ /** Firefox version to use. Defaults to the current Camoufox version.
54
+ * To prevent leaks, only use this for special cases.
55
+ */
56
+ ff_version?: number;
57
+ /** Whether to run the browser in headless mode. Defaults to `false`.
58
+ */
59
+ headless?: boolean;
60
+ /** Whether to enable running scripts in the main world.
61
+ * To use this, prepend "mw:" to the script: `page.evaluate("mw:" + script)`.
62
+ */
63
+ main_world_eval?: boolean;
64
+ /** Custom browser executable path. */
65
+ executable_path?: string | PathLike;
66
+ /** Firefox user preferences to set. */
67
+ firefox_user_prefs?: Record<string, any>;
68
+ /** Proxy to use for the browser.
69
+ * Note: If `geoip` is `true`, a request will be sent through this proxy to find the target IP.
70
+ */
71
+ proxy?: string | PlaywrightLaunchOptions['proxy'];
72
+ /** Cache previous pages, requests, etc. (uses more memory). */
73
+ enable_cache?: boolean;
74
+ /** Arguments to pass to the browser. */
75
+ args?: string[];
76
+ /** Environment variables to set. */
77
+ env?: Record<string, string | number | boolean>;
78
+ /** Prints the config being sent to Camoufox. */
79
+ debug?: boolean;
80
+ /** Virtual display number. Example: `":99"`. This is handled by Camoufox & AsyncCamoufox. */
81
+ virtual_display?: string;
82
+ /** Use a specific WebGL vendor/renderer pair. Passed as a tuple of `[vendor, renderer]`. */
83
+ webgl_config?: [string, string];
84
+ allow_addon_new_tab?: boolean;
85
+ /** Additional Firefox launch options. */
86
+ [key: string]: any;
87
+ }
88
+ export declare function launchOptions({ config, os, block_images, block_webrtc, block_webgl, disable_coop, webgl_config, geoip, humanize, locale, addons, fonts, custom_fonts_only, exclude_addons, screen, window, fingerprint, ff_version, headless, main_world_eval, executable_path, firefox_user_prefs, proxy, enable_cache, args, env, i_know_what_im_doing, debug, virtual_display, allow_addon_new_tab, ...launch_options }: LaunchOptions): Promise<Record<string, any>>;
89
+ export {};
package/dist/utils.js ADDED
@@ -0,0 +1,497 @@
1
+ // from browserforge.fingerprints import Fingerprint, Screen
2
+ // from screeninfo import get_monitors
3
+ // from ua_parser import user_agent_parser
4
+ import path from 'path';
5
+ import { addDefaultAddons, confirmPaths } from './addons.js';
6
+ import { InvalidOS, InvalidPropertyType, NonFirefoxFingerprint, UnknownProperty } from './exceptions.js';
7
+ import { fromBrowserforge, generateFingerprint, SUPPORTED_OS } from './fingerprints.js';
8
+ import { publicIP, validIPv4, validIPv6 } from './ip.js';
9
+ import { geoipAllowed, getGeolocation, handleLocales } from './locale.js';
10
+ import { OS_NAME, getPath, installedVerStr, launchPath } from './pkgman.js';
11
+ import { LeakWarning } from './warnings.js';
12
+ import { sampleWebGL } from './webgl/sample.js';
13
+ import { readFileSync } from 'fs';
14
+ import { join } from 'path';
15
+ import { UAParser } from 'ua-parser-js';
16
+ // Camoufox preferences to cache previous pages and requests
17
+ const CACHE_PREFS = {
18
+ 'browser.sessionhistory.max_entries': 10,
19
+ 'browser.sessionhistory.max_total_viewers': -1,
20
+ 'browser.cache.memory.enable': true,
21
+ 'browser.cache.disk_cache_ssl': true,
22
+ 'browser.cache.disk.smart_size.enabled': true,
23
+ };
24
+ function getEnvVars(configMap, userAgentOS) {
25
+ const envVars = {};
26
+ let updatedConfigData;
27
+ try {
28
+ updatedConfigData = new TextEncoder().encode(JSON.stringify(configMap));
29
+ }
30
+ catch (e) {
31
+ console.error(`Error updating config: ${e}`);
32
+ process.exit(1);
33
+ }
34
+ const chunkSize = OS_NAME === 'win' ? 2047 : 32767;
35
+ const configStr = new TextDecoder().decode(updatedConfigData);
36
+ for (let i = 0; i < configStr.length; i += chunkSize) {
37
+ const chunk = configStr.slice(i, i + chunkSize);
38
+ const envName = `CAMOU_CONFIG_${Math.floor(i / chunkSize) + 1}`;
39
+ try {
40
+ envVars[envName] = chunk;
41
+ }
42
+ catch (e) {
43
+ console.error(`Error setting ${envName}: ${e}`);
44
+ process.exit(1);
45
+ }
46
+ }
47
+ if (OS_NAME === 'lin') {
48
+ const fontconfigPath = getPath(path.join('fontconfig', userAgentOS));
49
+ envVars['FONTCONFIG_PATH'] = fontconfigPath;
50
+ }
51
+ return envVars;
52
+ }
53
+ export function getAsBooleanFromENV(name, defaultValue) {
54
+ const value = process.env[name];
55
+ if (value === 'false' || value === '0')
56
+ return false;
57
+ if (value)
58
+ return true;
59
+ return !!defaultValue;
60
+ }
61
+ function loadProperties(filePath) {
62
+ let propFile;
63
+ filePath = filePath?.toString();
64
+ if (filePath) {
65
+ propFile = path.join(path.dirname(filePath), 'properties.json');
66
+ }
67
+ else {
68
+ propFile = getPath('properties.json');
69
+ }
70
+ const propData = readFileSync(propFile).toString();
71
+ const propDict = JSON.parse(propData);
72
+ return propDict.reduce((acc, prop) => {
73
+ acc[prop.property] = prop.type;
74
+ return acc;
75
+ }, {});
76
+ }
77
+ function validateConfig(configMap, path) {
78
+ const propertyTypes = loadProperties(path);
79
+ for (const [key, value] of Object.entries(configMap)) {
80
+ const expectedType = propertyTypes[key];
81
+ if (!expectedType) {
82
+ throw new UnknownProperty(`Unknown property ${key} in config`);
83
+ }
84
+ if (!validateType(value, expectedType)) {
85
+ throw new InvalidPropertyType(`Invalid type for property ${key}. Expected ${expectedType}, got ${typeof value}`);
86
+ }
87
+ }
88
+ }
89
+ function validateType(value, expectedType) {
90
+ switch (expectedType) {
91
+ case 'str':
92
+ return typeof value === 'string';
93
+ case 'int':
94
+ return Number.isInteger(value);
95
+ case 'uint':
96
+ return Number.isInteger(value) && value >= 0;
97
+ case 'double':
98
+ return typeof value === 'number';
99
+ case 'bool':
100
+ return typeof value === 'boolean';
101
+ case 'array':
102
+ return Array.isArray(value);
103
+ case 'dict':
104
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
105
+ default:
106
+ return false;
107
+ }
108
+ }
109
+ function getTargetOS(config) {
110
+ if (config['navigator.userAgent']) {
111
+ return determineUAOS(config['navigator.userAgent']);
112
+ }
113
+ return OS_NAME;
114
+ }
115
+ function determineUAOS(userAgent) {
116
+ const parser = new UAParser(userAgent);
117
+ const parsedUA = parser.getOS().name;
118
+ if (!parsedUA) {
119
+ throw new Error("Could not determine OS from user agent");
120
+ }
121
+ if (parsedUA.startsWith("macOS")) {
122
+ return 'mac';
123
+ }
124
+ if (parsedUA.startsWith("Windows")) {
125
+ return 'win';
126
+ }
127
+ return 'lin';
128
+ }
129
+ function getScreenCons(headless) {
130
+ if (headless === false) {
131
+ return undefined;
132
+ }
133
+ // TODO - Implement getMonitors
134
+ // try {
135
+ // const monitors = getMonitors();
136
+ // if (!monitors.length) {
137
+ // return undefined;
138
+ // }
139
+ // const monitor = monitors.reduce((prev, curr) => (prev.width * prev.height > curr.width * curr.height ? prev : curr));
140
+ // return { maxWidth: monitor.width, maxHeight: monitor.height };
141
+ // } catch {
142
+ // return undefined;
143
+ // }
144
+ return undefined;
145
+ }
146
+ function updateFonts(config, targetOS) {
147
+ const fontsPath = join(import.meta.dirname, 'data-files', 'fonts.json');
148
+ const fonts = JSON.parse(readFileSync(fontsPath, 'utf-8'))[targetOS];
149
+ if (config.fonts) {
150
+ config.fonts = Array.from(new Set([...fonts, ...config.fonts]));
151
+ }
152
+ else {
153
+ config.fonts = fonts;
154
+ }
155
+ }
156
+ function checkCustomFingerprint(fingerprint) {
157
+ const parser = new UAParser(fingerprint.navigator.userAgent);
158
+ const browserName = parser.getBrowser().name || 'Non-Firefox';
159
+ if (browserName !== 'Firefox') {
160
+ throw new NonFirefoxFingerprint(`"${browserName}" fingerprints are not supported in Camoufox. Using fingerprints from a browser other than Firefox WILL lead to detection. If this is intentional, pass i_know_what_im_doing=True.`);
161
+ }
162
+ LeakWarning.warn('custom_fingerprint', false);
163
+ }
164
+ function validateOS(os) {
165
+ if (!os)
166
+ return undefined;
167
+ if (Array.isArray(os)) {
168
+ os.every(validateOS);
169
+ return [...os];
170
+ }
171
+ if (!SUPPORTED_OS.includes(os)) {
172
+ throw new InvalidOS(`Camoufox does not support the OS: '${os}'`);
173
+ }
174
+ return [os];
175
+ }
176
+ function cleanLocals(data) {
177
+ delete data.playwright;
178
+ delete data.persistentContext;
179
+ return data;
180
+ }
181
+ function mergeInto(target, source) {
182
+ Object.entries(source).forEach(([key, value]) => {
183
+ if (!(key in target)) {
184
+ target[key] = value;
185
+ }
186
+ });
187
+ }
188
+ function setInto(target, key, value) {
189
+ if (!(key in target)) {
190
+ target[key] = value;
191
+ }
192
+ }
193
+ function isDomainSet(config, ...properties) {
194
+ return properties.some(prop => {
195
+ if (prop.endsWith('.') || prop.endsWith(':')) {
196
+ return Object.keys(config).some(key => key.startsWith(prop));
197
+ }
198
+ return prop in config;
199
+ });
200
+ }
201
+ function warnManualConfig(config) {
202
+ if (isDomainSet(config, 'navigator.language', 'navigator.languages', 'headers.Accept-Language', 'locale:')) {
203
+ LeakWarning.warn('locale', false);
204
+ }
205
+ if (isDomainSet(config, 'geolocation:', 'timezone')) {
206
+ LeakWarning.warn('geolocation', false);
207
+ }
208
+ if (isDomainSet(config, 'headers.User-Agent')) {
209
+ LeakWarning.warn('header-ua', false);
210
+ }
211
+ if (isDomainSet(config, 'navigator.')) {
212
+ LeakWarning.warn('navigator', false);
213
+ }
214
+ if (isDomainSet(config, 'screen.', 'window.', 'document.body.')) {
215
+ LeakWarning.warn('viewport', false);
216
+ }
217
+ }
218
+ async function asyncAttachVD(browser, virtualDisplay) {
219
+ if (!virtualDisplay) {
220
+ return browser;
221
+ }
222
+ const originalClose = browser.close;
223
+ browser.close = async (...args) => {
224
+ await originalClose.apply(browser, ...args);
225
+ if (virtualDisplay) {
226
+ virtualDisplay.kill();
227
+ }
228
+ };
229
+ browser._virtualDisplay = virtualDisplay;
230
+ return browser;
231
+ }
232
+ export function syncAttachVD(browser, virtualDisplay) {
233
+ /**
234
+ * Attaches the virtual display to the sync browser cleanup
235
+ */
236
+ if (!virtualDisplay) { // Skip if no virtual display is provided
237
+ return browser;
238
+ }
239
+ const originalClose = browser.close;
240
+ browser.close = (...args) => {
241
+ originalClose.apply(browser, ...args);
242
+ if (virtualDisplay) {
243
+ virtualDisplay.kill();
244
+ }
245
+ };
246
+ browser._virtualDisplay = virtualDisplay;
247
+ return browser;
248
+ }
249
+ /**
250
+ * Convert a Playwright proxy string to a URL object.
251
+ *
252
+ * Implementation from https://github.com/microsoft/playwright/blob/3873b72ac1441ca691f7594f0ed705bd84518f93/packages/playwright-core/src/server/browserContext.ts#L737-L747
253
+ */
254
+ function getProxyUrl(proxy) {
255
+ if (!proxy)
256
+ return null;
257
+ if (typeof proxy === 'string') {
258
+ return new URL(proxy);
259
+ }
260
+ const { server, username, password } = proxy;
261
+ let url;
262
+ try {
263
+ // new URL('127.0.0.1:8080') throws
264
+ // new URL('localhost:8080') fails to parse host or protocol
265
+ // In both of these cases, we need to try re-parse URL with `http://` prefix.
266
+ url = new URL(server);
267
+ if (!url.host || !url.protocol)
268
+ url = new URL('http://' + server);
269
+ }
270
+ catch (e) {
271
+ url = new URL('http://' + server);
272
+ }
273
+ if (username)
274
+ url.username = username;
275
+ if (password)
276
+ url.password = password;
277
+ return url;
278
+ }
279
+ export async function launchOptions({ config, os, block_images, block_webrtc, block_webgl, disable_coop, webgl_config, geoip, humanize, locale, addons, fonts, custom_fonts_only, exclude_addons, screen, window, fingerprint, ff_version, headless, main_world_eval, executable_path, firefox_user_prefs, proxy, enable_cache, args, env, i_know_what_im_doing, debug, virtual_display, allow_addon_new_tab, ...launch_options }) {
280
+ // Build the config
281
+ if (!config) {
282
+ config = {};
283
+ }
284
+ // Set default values for optional arguments
285
+ if (headless === undefined) {
286
+ headless = false;
287
+ }
288
+ if (!addons) {
289
+ addons = [];
290
+ }
291
+ if (!args) {
292
+ args = [];
293
+ }
294
+ if (!firefox_user_prefs) {
295
+ firefox_user_prefs = {};
296
+ }
297
+ if (custom_fonts_only === undefined) {
298
+ custom_fonts_only = false;
299
+ }
300
+ if (i_know_what_im_doing === undefined) {
301
+ i_know_what_im_doing = false;
302
+ }
303
+ if (!env) {
304
+ env = process.env;
305
+ }
306
+ if (typeof executable_path === 'string') {
307
+ // Convert executable path to a Path object
308
+ executable_path = path.resolve(executable_path);
309
+ }
310
+ // Handle virtual display
311
+ if (virtual_display) {
312
+ env['DISPLAY'] = virtual_display;
313
+ }
314
+ // Warn the user for manual config settings
315
+ if (!i_know_what_im_doing) {
316
+ warnManualConfig(config);
317
+ }
318
+ const operatingSystems = validateOS(os);
319
+ // webgl_config requires OS to be set
320
+ if (!operatingSystems && webgl_config) {
321
+ throw new Error('OS must be set when using webgl_config');
322
+ }
323
+ // Add the default addons
324
+ addDefaultAddons(addons, exclude_addons);
325
+ // Confirm all addon paths are valid
326
+ if (addons.length > 0) {
327
+ confirmPaths(addons);
328
+ config['addons'] = addons;
329
+ }
330
+ // Get the Firefox version
331
+ let ff_version_str;
332
+ if (ff_version) {
333
+ ff_version_str = ff_version.toString();
334
+ LeakWarning.warn('ff_version', i_know_what_im_doing);
335
+ }
336
+ else {
337
+ ff_version_str = installedVerStr().split('.', 1)[0];
338
+ }
339
+ // Generate a fingerprint
340
+ if (!fingerprint) {
341
+ fingerprint = generateFingerprint(window, {
342
+ screen: screen || getScreenCons(headless || 'DISPLAY' in env),
343
+ operatingSystems,
344
+ });
345
+ }
346
+ else {
347
+ // Or use the one passed by the user
348
+ if (!i_know_what_im_doing) {
349
+ checkCustomFingerprint(fingerprint);
350
+ }
351
+ }
352
+ // Inject the fingerprint into the config
353
+ mergeInto(config, fromBrowserforge(fingerprint, ff_version_str));
354
+ const targetOS = getTargetOS(config);
355
+ // Set a random window.history.length
356
+ setInto(config, 'window.history.length', Math.floor(Math.random() * 5) + 1);
357
+ // Update fonts list
358
+ if (fonts) {
359
+ config['fonts'] = fonts;
360
+ }
361
+ if (custom_fonts_only) {
362
+ firefox_user_prefs['gfx.bundled-fonts.activate'] = 0;
363
+ if (fonts) {
364
+ // The user has passed their own fonts, and OS fonts are disabled.
365
+ LeakWarning.warn('custom_fonts_only');
366
+ }
367
+ else {
368
+ // OS fonts are disabled, and the user has not passed their own fonts either.
369
+ throw new Error('No custom fonts were passed, but `custom_fonts_only` is enabled.');
370
+ }
371
+ }
372
+ else {
373
+ updateFonts(config, targetOS);
374
+ }
375
+ // Set a fixed font spacing seed
376
+ setInto(config, 'fonts:spacing_seed', Math.floor(Math.random() * 1_073_741_824));
377
+ // Handle proxy
378
+ const proxyUrl = getProxyUrl(proxy);
379
+ // Set geolocation
380
+ if (geoip) {
381
+ geoipAllowed();
382
+ // Find the user's IP address
383
+ geoip = await publicIP(proxyUrl?.href);
384
+ // Spoof WebRTC if not blocked
385
+ if (!block_webrtc) {
386
+ if (validIPv4(geoip)) {
387
+ setInto(config, 'webrtc:ipv4', geoip);
388
+ firefox_user_prefs['network.dns.disableIPv6'] = true;
389
+ }
390
+ else if (validIPv6(geoip)) {
391
+ setInto(config, 'webrtc:ipv6', geoip);
392
+ }
393
+ }
394
+ const geolocation = await getGeolocation(geoip);
395
+ config = { ...config, ...geolocation.asConfig() };
396
+ }
397
+ // Raise a warning when a proxy is being used without spoofing geolocation.
398
+ // This is a very bad idea; the warning cannot be ignored with i_know_what_im_doing.
399
+ if (proxyUrl &&
400
+ !proxyUrl.hostname.includes('localhost') &&
401
+ !isDomainSet(config, 'geolocation:')) {
402
+ LeakWarning.warn('proxy_without_geoip');
403
+ }
404
+ // Set locale
405
+ if (locale) {
406
+ handleLocales(locale, config);
407
+ }
408
+ // Pass the humanize option
409
+ if (humanize) {
410
+ setInto(config, 'humanize', true);
411
+ if (typeof humanize === 'number') {
412
+ setInto(config, 'humanize:maxTime', humanize);
413
+ }
414
+ }
415
+ // Enable the main world context creation
416
+ if (main_world_eval) {
417
+ setInto(config, 'allowMainWorld', true);
418
+ }
419
+ // Set Firefox user preferences
420
+ if (block_images) {
421
+ LeakWarning.warn('block_images', i_know_what_im_doing);
422
+ firefox_user_prefs['permissions.default.image'] = 2;
423
+ }
424
+ if (block_webrtc) {
425
+ firefox_user_prefs['media.peerconnection.enabled'] = false;
426
+ }
427
+ if (disable_coop) {
428
+ LeakWarning.warn('disable_coop', i_know_what_im_doing);
429
+ firefox_user_prefs['browser.tabs.remote.useCrossOriginOpenerPolicy'] = false;
430
+ }
431
+ // Allow allow_webgl parameter for backwards compatibility
432
+ if (block_webgl || launch_options.allow_webgl === false) {
433
+ firefox_user_prefs['webgl.disabled'] = true;
434
+ LeakWarning.warn('block_webgl', i_know_what_im_doing);
435
+ }
436
+ else {
437
+ // If the user has provided a specific WebGL vendor/renderer pair, use it
438
+ let webgl_fp;
439
+ if (webgl_config) {
440
+ webgl_fp = await sampleWebGL(targetOS, ...webgl_config);
441
+ }
442
+ else {
443
+ webgl_fp = await sampleWebGL(targetOS);
444
+ }
445
+ const { webGl2Enabled, ...webGlConfig } = webgl_fp;
446
+ // Merge the WebGL fingerprint into the config
447
+ mergeInto(config, webGlConfig);
448
+ // Set the WebGL preferences
449
+ mergeInto(firefox_user_prefs, {
450
+ 'webgl.enable-webgl2': webGl2Enabled,
451
+ 'webgl.force-enabled': true,
452
+ });
453
+ }
454
+ // Canvas anti-fingerprinting
455
+ mergeInto(config, {
456
+ 'canvas:aaOffset': Math.floor(Math.random() * 101) - 50, // nosec
457
+ 'canvas:aaCapOffset': true,
458
+ });
459
+ // Cache previous pages, requests, etc (uses more memory)
460
+ if (enable_cache) {
461
+ mergeInto(firefox_user_prefs, CACHE_PREFS);
462
+ }
463
+ // Print the config if debug is enabled
464
+ if (debug) {
465
+ console.debug('[DEBUG] Config:');
466
+ console.debug(config);
467
+ }
468
+ // Validate the config
469
+ validateConfig(config, executable_path);
470
+ //Prepare environment variables to pass to Camoufox
471
+ const env_vars = {
472
+ ...getEnvVars(config, targetOS),
473
+ ...process.env,
474
+ };
475
+ // Prepare the executable path
476
+ if (executable_path) {
477
+ executable_path = executable_path.toString();
478
+ }
479
+ else {
480
+ executable_path = launchPath();
481
+ }
482
+ const out = {
483
+ "executablePath": executable_path,
484
+ "args": args,
485
+ "env": env_vars,
486
+ "firefoxUserPrefs": firefox_user_prefs,
487
+ "proxy": proxyUrl ? {
488
+ server: proxyUrl.origin,
489
+ username: proxyUrl.username,
490
+ password: proxyUrl.password,
491
+ bypass: typeof proxy === 'string' ? undefined : proxy?.bypass,
492
+ } : undefined,
493
+ "headless": headless,
494
+ ...launch_options,
495
+ };
496
+ return out;
497
+ }
@@ -0,0 +1,16 @@
1
+ export declare class VirtualDisplay {
2
+ private debug;
3
+ private proc;
4
+ private _display;
5
+ constructor(debug?: boolean);
6
+ private get xvfb_args();
7
+ private get xvfb_path();
8
+ private get xvfb_cmd();
9
+ private execute_xvfb;
10
+ get(): string;
11
+ kill(): void;
12
+ static _get_lock_files(): string[];
13
+ private static _free_display;
14
+ private get display();
15
+ private static assert_linux;
16
+ }