UNPKG

brave-real-playwright-core

Version:

Brave-optimized Playwright Core (v1.55.0) with comprehensive stealth patches and error stack sanitization

329 lines (328 loc) 15.1 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var browserType_exports = {}; __export(browserType_exports, { BrowserType: () => BrowserType, kNoXServerRunningError: () => kNoXServerRunningError }); module.exports = __toCommonJS(browserType_exports); var import_fs = __toESM(require("fs")); var import_os = __toESM(require("os")); var import_path = __toESM(require("path")); var import_browserContext = require("./browserContext"); var import_debug = require("./utils/debug"); var import_assert = require("../utils/isomorphic/assert"); var import_manualPromise = require("../utils/isomorphic/manualPromise"); var import_time = require("../utils/isomorphic/time"); var import_fileUtils = require("./utils/fileUtils"); var import_helper = require("./helper"); var import_instrumentation = require("./instrumentation"); var import_pipeTransport = require("./pipeTransport"); var import_processLauncher = require("./utils/processLauncher"); var import_protocolError = require("./protocolError"); var import_registry = require("./registry"); var import_socksClientCertificatesInterceptor = require("./socksClientCertificatesInterceptor"); var import_transport = require("./transport"); var import_debugLogger = require("./utils/debugLogger"); const kNoXServerRunningError = "Looks like you launched a headed browser without having a XServer running.\nSet either 'headless: true' or use 'xvfb-run <your-playwright-app>' before running Playwright.\n\n<3 Playwright Team"; class BrowserType extends import_instrumentation.SdkObject { constructor(parent, browserName) { super(parent, "browser-type"); this.attribution.browserType = this; this._name = browserName; this.logName = "browser"; } executablePath() { return import_registry.registry.findExecutable(this._name).executablePath(this.attribution.playwright.options.sdkLanguage) || ""; } name() { return this._name; } async launch(progress, options, protocolLogger) { options = this._validateLaunchOptions(options); const seleniumHubUrl = options.__testHookSeleniumRemoteURL || process.env.SELENIUM_REMOTE_URL; if (seleniumHubUrl) return this._launchWithSeleniumHub(progress, seleniumHubUrl, options); return this._innerLaunchWithRetries(progress, options, void 0, import_helper.helper.debugProtocolLogger(protocolLogger)).catch((e) => { throw this._rewriteStartupLog(e); }); } async launchPersistentContext(progress, userDataDir, options) { const launchOptions = this._validateLaunchOptions(options); let clientCertificatesProxy; if (options.clientCertificates?.length) { clientCertificatesProxy = await import_socksClientCertificatesInterceptor.ClientCertificatesProxy.create(progress, options); launchOptions.proxyOverride = clientCertificatesProxy.proxySettings(); options = { ...options }; options.internalIgnoreHTTPSErrors = true; } try { const browser = await this._innerLaunchWithRetries(progress, launchOptions, options, import_helper.helper.debugProtocolLogger(), userDataDir).catch((e) => { throw this._rewriteStartupLog(e); }); browser._defaultContext._clientCertificatesProxy = clientCertificatesProxy; return browser._defaultContext; } catch (error) { await clientCertificatesProxy?.close().catch(() => { }); throw error; } } async _innerLaunchWithRetries(progress, options, persistent, protocolLogger, userDataDir) { try { return await this._innerLaunch(progress, options, persistent, protocolLogger, userDataDir); } catch (error) { const errorMessage = typeof error === "object" && typeof error.message === "string" ? error.message : ""; if (errorMessage.includes("Inconsistency detected by ld.so")) { progress.log(`<restarting browser due to hitting race condition in glibc>`); return this._innerLaunch(progress, options, persistent, protocolLogger, userDataDir); } throw error; } } async _innerLaunch(progress, options, persistent, protocolLogger, maybeUserDataDir) { options.proxy = options.proxy ? (0, import_browserContext.normalizeProxySettings)(options.proxy) : void 0; const browserLogsCollector = new import_debugLogger.RecentLogsCollector(); const { browserProcess, userDataDir, artifactsDir, transport } = await this._launchProcess(progress, options, !!persistent, browserLogsCollector, maybeUserDataDir); try { if (options.__testHookBeforeCreateBrowser) await progress.race(options.__testHookBeforeCreateBrowser()); const browserOptions = { name: this._name, isChromium: this._name === "chromium", channel: options.channel, slowMo: options.slowMo, persistent, headful: !options.headless, artifactsDir, downloadsPath: options.downloadsPath || artifactsDir, tracesDir: options.tracesDir || artifactsDir, browserProcess, customExecutablePath: options.executablePath, proxy: options.proxy, protocolLogger, browserLogsCollector, wsEndpoint: transport instanceof import_transport.WebSocketTransport ? transport.wsEndpoint : void 0, originalLaunchOptions: options }; if (persistent) (0, import_browserContext.validateBrowserContextOptions)(persistent, browserOptions); copyTestHooks(options, browserOptions); const browser = await progress.race(this.connectToTransport(transport, browserOptions, browserLogsCollector)); browser._userDataDirForTest = userDataDir; if (persistent && !options.ignoreAllDefaultArgs) await browser._defaultContext._loadDefaultContext(progress); return browser; } catch (error) { await browserProcess.close().catch(() => { }); throw error; } } async _prepareToLaunch(options, isPersistent, userDataDir) { const { ignoreDefaultArgs, ignoreAllDefaultArgs, args = [], executablePath = null } = options; await this._createArtifactDirs(options); const tempDirectories = []; const artifactsDir = await import_fs.default.promises.mkdtemp(import_path.default.join(import_os.default.tmpdir(), "playwright-artifacts-")); tempDirectories.push(artifactsDir); if (userDataDir) { (0, import_assert.assert)(import_path.default.isAbsolute(userDataDir), "userDataDir must be an absolute path"); if (!await (0, import_fileUtils.existsAsync)(userDataDir)) await import_fs.default.promises.mkdir(userDataDir, { recursive: true, mode: 448 }); } else { userDataDir = await import_fs.default.promises.mkdtemp(import_path.default.join(import_os.default.tmpdir(), `playwright_${this._name}dev_profile-`)); tempDirectories.push(userDataDir); } await this.prepareUserDataDir(options, userDataDir); const browserArguments = []; if (ignoreAllDefaultArgs) browserArguments.push(...args); else if (ignoreDefaultArgs) browserArguments.push(...this.defaultArgs(options, isPersistent, userDataDir).filter((arg) => ignoreDefaultArgs.indexOf(arg) === -1)); else browserArguments.push(...this.defaultArgs(options, isPersistent, userDataDir)); let executable; if (executablePath) { if (!await (0, import_fileUtils.existsAsync)(executablePath)) throw new Error(`Failed to launch ${this._name} because executable doesn't exist at ${executablePath}`); executable = executablePath; } else { const registryExecutable = import_registry.registry.findExecutable(this.getExecutableName(options)); if (!registryExecutable || registryExecutable.browserName !== this._name) throw new Error(`Unsupported ${this._name} channel "${options.channel}"`); executable = registryExecutable.executablePathOrDie(this.attribution.playwright.options.sdkLanguage); await import_registry.registry.validateHostRequirementsForExecutablesIfNeeded([registryExecutable], this.attribution.playwright.options.sdkLanguage); } return { executable, browserArguments, userDataDir, artifactsDir, tempDirectories }; } async _launchProcess(progress, options, isPersistent, browserLogsCollector, userDataDir) { const { handleSIGINT = true, handleSIGTERM = true, handleSIGHUP = true } = options; const env = options.env ? (0, import_processLauncher.envArrayToObject)(options.env) : process.env; const prepared = await progress.race(this._prepareToLaunch(options, isPersistent, userDataDir)); let transport = void 0; let browserProcess = void 0; const exitPromise = new import_manualPromise.ManualPromise(); const { launchedProcess, gracefullyClose, kill } = await (0, import_processLauncher.launchProcess)({ command: prepared.executable, args: prepared.browserArguments, env: this.amendEnvironment(env, prepared.userDataDir, isPersistent), handleSIGINT, handleSIGTERM, handleSIGHUP, log: (message) => { progress.log(message); browserLogsCollector.log(message); }, stdio: "pipe", tempDirectories: prepared.tempDirectories, attemptToGracefullyClose: async () => { if (options.__testHookGracefullyClose) await options.__testHookGracefullyClose(); if (transport) { this.attemptToGracefullyCloseBrowser(transport); } else { throw new Error("Force-killing the browser because no transport is available to gracefully close it."); } }, onExit: (exitCode, signal) => { exitPromise.resolve(); if (browserProcess && browserProcess.onclose) browserProcess.onclose(exitCode, signal); } }); async function closeOrKill(timeout) { let timer; try { await Promise.race([ gracefullyClose(), new Promise((resolve, reject) => timer = setTimeout(reject, timeout)) ]); } catch (ignored) { await kill().catch((ignored2) => { }); } finally { clearTimeout(timer); } } browserProcess = { onclose: void 0, process: launchedProcess, close: () => closeOrKill(options.__testHookBrowserCloseTimeout || import_time.DEFAULT_PLAYWRIGHT_TIMEOUT), kill }; try { const { wsEndpoint } = await progress.race([ this.waitForReadyState(options, browserLogsCollector), exitPromise.then(() => ({ wsEndpoint: void 0 })) ]); if (options.cdpPort !== void 0 || !this.supportsPipeTransport()) { transport = await import_transport.WebSocketTransport.connect(progress, wsEndpoint); } else { const stdio = launchedProcess.stdio; transport = new import_pipeTransport.PipeTransport(stdio[3], stdio[4]); } return { browserProcess, artifactsDir: prepared.artifactsDir, userDataDir: prepared.userDataDir, transport }; } catch (error) { await closeOrKill(import_time.DEFAULT_PLAYWRIGHT_TIMEOUT).catch(() => { }); throw error; } } async _createArtifactDirs(options) { if (options.downloadsPath) await import_fs.default.promises.mkdir(options.downloadsPath, { recursive: true }); if (options.tracesDir) await import_fs.default.promises.mkdir(options.tracesDir, { recursive: true }); } async connectOverCDP(progress, endpointURL, options) { throw new Error("CDP connections are only supported by Chromium"); } async _launchWithSeleniumHub(progress, hubUrl, options) { throw new Error("Connecting to SELENIUM_REMOTE_URL is only supported by Chromium"); } _validateLaunchOptions(options) { const { devtools = false } = options; let { headless = !devtools, downloadsPath, proxy } = options; if ((0, import_debug.debugMode)() === "inspector") headless = false; if (downloadsPath && !import_path.default.isAbsolute(downloadsPath)) downloadsPath = import_path.default.join(process.cwd(), downloadsPath); if (options.socksProxyPort) proxy = { server: `socks5://127.0.0.1:${options.socksProxyPort}` }; return { ...options, devtools, headless, downloadsPath, proxy }; } _createUserDataDirArgMisuseError(userDataDirArg) { switch (this.attribution.playwright.options.sdkLanguage) { case "java": return new Error(`Pass userDataDir parameter to 'BrowserType.launchPersistentContext(userDataDir, options)' instead of specifying '${userDataDirArg}' argument`); case "python": return new Error(`Pass user_data_dir parameter to 'browser_type.launch_persistent_context(user_data_dir, **kwargs)' instead of specifying '${userDataDirArg}' argument`); case "csharp": return new Error(`Pass userDataDir parameter to 'BrowserType.LaunchPersistentContextAsync(userDataDir, options)' instead of specifying '${userDataDirArg}' argument`); default: return new Error(`Pass userDataDir parameter to 'browserType.launchPersistentContext(userDataDir, options)' instead of specifying '${userDataDirArg}' argument`); } } _rewriteStartupLog(error) { if (!(0, import_protocolError.isProtocolError)(error)) return error; return this.doRewriteStartupLog(error); } async waitForReadyState(options, browserLogsCollector) { return {}; } async prepareUserDataDir(options, userDataDir) { } supportsPipeTransport() { return true; } getExecutableName(options) { return options.channel || this._name; } } function copyTestHooks(from, to) { for (const [key, value] of Object.entries(from)) { if (key.startsWith("__testHook")) to[key] = value; } } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { BrowserType, kNoXServerRunningError });