UNPKG

playwright-core

Version:

A high-level API to automate web browsers

434 lines • 19.5 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.spawnAsync = exports.validateHostRequirements = void 0; /** * Copyright (c) Microsoft Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const fs_1 = __importDefault(require("fs")); const util = __importStar(require("util")); const path_1 = __importDefault(require("path")); const os = __importStar(require("os")); const child_process_1 = require("child_process"); const ubuntuVersion_1 = require("../utils/ubuntuVersion"); const binaryPaths_1 = require("../utils/binaryPaths"); const accessAsync = util.promisify(fs_1.default.access.bind(fs_1.default)); const checkExecutable = (filePath) => accessAsync(filePath, fs_1.default.constants.X_OK).then(() => true).catch(e => false); const statAsync = util.promisify(fs_1.default.stat.bind(fs_1.default)); const readdirAsync = util.promisify(fs_1.default.readdir.bind(fs_1.default)); async function validateHostRequirements(registry, browserName) { const ubuntuVersion = await ubuntuVersion_1.getUbuntuVersion(); if (browserName === 'firefox' && ubuntuVersion === '16.04') throw new Error(`Cannot launch firefox on Ubuntu 16.04! Minimum required Ubuntu version for Firefox browser is 18.04`); if (os.platform() === 'linux') return await validateDependenciesLinux(registry, browserName); if (os.platform() === 'win32' && os.arch() === 'x64') return await validateDependenciesWindows(registry, browserName); } exports.validateHostRequirements = validateHostRequirements; const DL_OPEN_LIBRARIES = { chromium: [], webkit: ['libGLESv2.so.2', 'libx264.so'], firefox: [], clank: [], ffmpeg: [], }; function isSupportedWindowsVersion() { if (os.platform() !== 'win32' || os.arch() !== 'x64') return false; const [major, minor] = os.release().split('.').map(token => parseInt(token, 10)); // This is based on: https://stackoverflow.com/questions/42524606/how-to-get-windows-version-using-node-js/44916050#44916050 // The table with versions is taken from: https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexw#remarks // Windows 7 is not supported and is encoded as `6.1`. return major > 6 || (major === 6 && minor > 1); } async function validateDependenciesWindows(registry, browserName) { const directoryPaths = registry.windowsExeAndDllDirectories(browserName); const lddPaths = []; for (const directoryPath of directoryPaths) lddPaths.push(...(await executablesOrSharedLibraries(directoryPath))); const allMissingDeps = await Promise.all(lddPaths.map(lddPath => missingFileDependenciesWindows(lddPath))); const missingDeps = new Set(); for (const deps of allMissingDeps) { for (const dep of deps) missingDeps.add(dep); } if (!missingDeps.size) return; let isCrtMissing = false; let isMediaFoundationMissing = false; for (const dep of missingDeps) { if (dep.startsWith('api-ms-win-crt') || dep === 'vcruntime140.dll' || dep === 'vcruntime140_1.dll' || dep === 'msvcp140.dll') isCrtMissing = true; else if (dep === 'mf.dll' || dep === 'mfplat.dll' || dep === 'msmpeg2vdec.dll' || dep === 'evr.dll' || dep === 'avrt.dll') isMediaFoundationMissing = true; } const details = []; if (isCrtMissing) { details.push(`Some of the Universal C Runtime files cannot be found on the system. You can fix`, `that by installing Microsoft Visual C++ Redistributable for Visual Studio from:`, `https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads`, ``); } if (isMediaFoundationMissing) { details.push(`Some of the Media Foundation files cannot be found on the system. If you are`, `on Windows Server try fixing this by running the following command in PowerShell`, `as Administrator:`, ``, ` Install-WindowsFeature Server-Media-Foundation`, ``, `For Windows N editions visit:`, `https://support.microsoft.com/en-us/help/3145500/media-feature-pack-list-for-windows-n-editions`, ``); } details.push(`Full list of missing libraries:`, ` ${[...missingDeps].join('\n ')}`, ``); const message = `Host system is missing dependencies!\n\n${details.join('\n')}`; if (isSupportedWindowsVersion()) { throw new Error(message); } else { console.warn(`WARNING: running on unsupported windows version!`); console.warn(message); } } async function validateDependenciesLinux(registry, browserName) { const directoryPaths = registry.linuxLddDirectories(browserName); const lddPaths = []; for (const directoryPath of directoryPaths) lddPaths.push(...(await executablesOrSharedLibraries(directoryPath))); const allMissingDeps = await Promise.all(lddPaths.map(lddPath => missingFileDependencies(lddPath, directoryPaths))); const missingDeps = new Set(); for (const deps of allMissingDeps) { for (const dep of deps) missingDeps.add(dep); } for (const dep of (await missingDLOPENLibraries(browserName))) missingDeps.add(dep); if (!missingDeps.size) return; // Check Ubuntu version. const missingPackages = new Set(); const ubuntuVersion = await ubuntuVersion_1.getUbuntuVersion(); let libraryToPackageNameMapping = null; if (ubuntuVersion === '18.04') libraryToPackageNameMapping = LIBRARY_TO_PACKAGE_NAME_UBUNTU_18_04; else if (ubuntuVersion === '20.04') libraryToPackageNameMapping = LIBRARY_TO_PACKAGE_NAME_UBUNTU_20_04; libraryToPackageNameMapping = Object.assign({}, libraryToPackageNameMapping, MANUAL_LIBRARY_TO_PACKAGE_NAME_UBUNTU); if (libraryToPackageNameMapping) { // Translate missing dependencies to package names to install with apt. for (const missingDep of missingDeps) { const packageName = libraryToPackageNameMapping[missingDep]; if (packageName) { missingPackages.add(packageName); missingDeps.delete(missingDep); } } } let missingPackagesMessage = ''; if (missingPackages.size) { missingPackagesMessage = [ ` Install missing packages with:`, ` sudo apt-get install ${[...missingPackages].join('\\\n ')}`, ``, ``, ].join('\n'); } let missingDependenciesMessage = ''; if (missingDeps.size) { const header = missingPackages.size ? `Missing libraries we didn't find packages for:` : `Missing libraries are:`; missingDependenciesMessage = [ ` ${header}`, ` ${[...missingDeps].join('\n ')}`, ``, ].join('\n'); } throw new Error('Host system is missing dependencies!\n\n' + missingPackagesMessage + missingDependenciesMessage); } function isSharedLib(basename) { switch (os.platform()) { case 'linux': return basename.endsWith('.so') || basename.includes('.so.'); case 'win32': return basename.endsWith('.dll'); default: return false; } } async function executablesOrSharedLibraries(directoryPath) { const allPaths = (await readdirAsync(directoryPath)).map(file => path_1.default.resolve(directoryPath, file)); const allStats = await Promise.all(allPaths.map(aPath => statAsync(aPath))); const filePaths = allPaths.filter((aPath, index) => allStats[index].isFile()); const executablersOrLibraries = (await Promise.all(filePaths.map(async (filePath) => { const basename = path_1.default.basename(filePath).toLowerCase(); if (isSharedLib(basename)) return filePath; if (await checkExecutable(filePath)) return filePath; return false; }))).filter(Boolean); return executablersOrLibraries; } async function missingFileDependenciesWindows(filePath) { const executable = binaryPaths_1.printDepsWindowsExecutable(); if (!executable) return []; const dirname = path_1.default.dirname(filePath); const { stdout, code } = await spawnAsync(executable, [filePath], { cwd: dirname, env: { ...process.env, LD_LIBRARY_PATH: process.env.LD_LIBRARY_PATH ? `${process.env.LD_LIBRARY_PATH}:${dirname}` : dirname, }, }); if (code !== 0) return []; const missingDeps = stdout.split('\n').map(line => line.trim()).filter(line => line.endsWith('not found') && line.includes('=>')).map(line => line.split('=>')[0].trim().toLowerCase()); return missingDeps; } async function missingFileDependencies(filePath, extraLDPaths) { const dirname = path_1.default.dirname(filePath); let LD_LIBRARY_PATH = extraLDPaths.join(':'); if (process.env.LD_LIBRARY_PATH) LD_LIBRARY_PATH = `${process.env.LD_LIBRARY_PATH}:${LD_LIBRARY_PATH}`; const { stdout, code } = await spawnAsync('ldd', [filePath], { cwd: dirname, env: { ...process.env, LD_LIBRARY_PATH, }, }); if (code !== 0) return []; const missingDeps = stdout.split('\n').map(line => line.trim()).filter(line => line.endsWith('not found') && line.includes('=>')).map(line => line.split('=>')[0].trim()); return missingDeps; } async function missingDLOPENLibraries(browserName) { const libraries = DL_OPEN_LIBRARIES[browserName]; if (!libraries.length) return []; // NOTE: Using full-qualified path to `ldconfig` since `/sbin` is not part of the // default PATH in CRON. // @see https://github.com/microsoft/playwright/issues/3397 const { stdout, code, error } = await spawnAsync('/sbin/ldconfig', ['-p'], {}); if (code !== 0 || error) return []; const isLibraryAvailable = (library) => stdout.toLowerCase().includes(library.toLowerCase()); return libraries.filter(library => !isLibraryAvailable(library)); } function spawnAsync(cmd, args, options) { const process = child_process_1.spawn(cmd, args, options); return new Promise(resolve => { let stdout = ''; let stderr = ''; process.stdout.on('data', data => stdout += data); process.stderr.on('data', data => stderr += data); process.on('close', code => resolve({ stdout, stderr, code })); process.on('error', error => resolve({ stdout, stderr, code: 0, error })); }); } exports.spawnAsync = spawnAsync; // This list is generted with the following program: // ./utils/linux-browser-dependencies/run.sh ubuntu:18.04 const LIBRARY_TO_PACKAGE_NAME_UBUNTU_18_04 = { 'libasound.so.2': 'libasound2', 'libatk-1.0.so.0': 'libatk1.0-0', 'libatk-bridge-2.0.so.0': 'libatk-bridge2.0-0', 'libatspi.so.0': 'libatspi2.0-0', 'libbrotlidec.so.1': 'libbrotli1', 'libcairo-gobject.so.2': 'libcairo-gobject2', 'libcairo.so.2': 'libcairo2', 'libcups.so.2': 'libcups2', 'libdbus-1.so.3': 'libdbus-1-3', 'libdbus-glib-1.so.2': 'libdbus-glib-1-2', 'libdrm.so.2': 'libdrm2', 'libEGL.so.1': 'libegl1', 'libenchant.so.1': 'libenchant1c2a', 'libepoxy.so.0': 'libepoxy0', 'libevent-2.1.so.6': 'libevent-2.1-6', 'libfontconfig.so.1': 'libfontconfig1', 'libfreetype.so.6': 'libfreetype6', 'libgbm.so.1': 'libgbm1', 'libgdk_pixbuf-2.0.so.0': 'libgdk-pixbuf2.0-0', 'libgdk-3.so.0': 'libgtk-3-0', 'libgdk-x11-2.0.so.0': 'libgtk2.0-0', 'libgio-2.0.so.0': 'libglib2.0-0', 'libGL.so.1': 'libgl1', 'libGLESv2.so.2': 'libgles2', 'libglib-2.0.so.0': 'libglib2.0-0', 'libgmodule-2.0.so.0': 'libglib2.0-0', 'libgobject-2.0.so.0': 'libglib2.0-0', 'libgstapp-1.0.so.0': 'libgstreamer-plugins-base1.0-0', 'libgstaudio-1.0.so.0': 'libgstreamer-plugins-base1.0-0', 'libgstbase-1.0.so.0': 'libgstreamer1.0-0', 'libgstcodecparsers-1.0.so.0': 'libgstreamer-plugins-bad1.0-0', 'libgstfft-1.0.so.0': 'libgstreamer-plugins-base1.0-0', 'libgstgl-1.0.so.0': 'libgstreamer-gl1.0-0', 'libgstpbutils-1.0.so.0': 'libgstreamer-plugins-base1.0-0', 'libgstreamer-1.0.so.0': 'libgstreamer1.0-0', 'libgsttag-1.0.so.0': 'libgstreamer-plugins-base1.0-0', 'libgstvideo-1.0.so.0': 'libgstreamer-plugins-base1.0-0', 'libgthread-2.0.so.0': 'libglib2.0-0', 'libgtk-3.so.0': 'libgtk-3-0', 'libgtk-x11-2.0.so.0': 'libgtk2.0-0', 'libharfbuzz-icu.so.0': 'libharfbuzz-icu0', 'libharfbuzz.so.0': 'libharfbuzz0b', 'libhyphen.so.0': 'libhyphen0', 'libicudata.so.60': 'libicu60', 'libicui18n.so.60': 'libicu60', 'libicuuc.so.60': 'libicu60', 'libjpeg.so.8': 'libjpeg-turbo8', 'libnotify.so.4': 'libnotify4', 'libnspr4.so': 'libnspr4', 'libnss3.so': 'libnss3', 'libnssutil3.so': 'libnss3', 'libopenjp2.so.7': 'libopenjp2-7', 'libopus.so.0': 'libopus0', 'libpango-1.0.so.0': 'libpango-1.0-0', 'libpangocairo-1.0.so.0': 'libpangocairo-1.0-0', 'libpangoft2-1.0.so.0': 'libpangoft2-1.0-0', 'libpng16.so.16': 'libpng16-16', 'libsecret-1.so.0': 'libsecret-1-0', 'libsmime3.so': 'libnss3', 'libvpx.so.5': 'libvpx5', 'libwayland-client.so.0': 'libwayland-client0', 'libwayland-egl.so.1': 'libwayland-egl1', 'libwayland-server.so.0': 'libwayland-server0', 'libwebp.so.6': 'libwebp6', 'libwebpdemux.so.2': 'libwebpdemux2', 'libwoff2dec.so.1.0.2': 'libwoff1', 'libX11-xcb.so.1': 'libx11-xcb1', 'libX11.so.6': 'libx11-6', 'libxcb-dri3.so.0': 'libxcb-dri3-0', 'libxcb-shm.so.0': 'libxcb-shm0', 'libxcb.so.1': 'libxcb1', 'libXcomposite.so.1': 'libxcomposite1', 'libXcursor.so.1': 'libxcursor1', 'libXdamage.so.1': 'libxdamage1', 'libXext.so.6': 'libxext6', 'libXfixes.so.3': 'libxfixes3', 'libXi.so.6': 'libxi6', 'libxkbcommon.so.0': 'libxkbcommon0', 'libxml2.so.2': 'libxml2', 'libXrandr.so.2': 'libxrandr2', 'libXrender.so.1': 'libxrender1', 'libxslt.so.1': 'libxslt1.1', 'libXt.so.6': 'libxt6', 'libXtst.so.6': 'libxtst6', }; // This list is generted with the following program: // ./utils/linux-browser-dependencies/run.sh ubuntu:20.04 const LIBRARY_TO_PACKAGE_NAME_UBUNTU_20_04 = { 'libasound.so.2': 'libasound2', 'libatk-1.0.so.0': 'libatk1.0-0', 'libatk-bridge-2.0.so.0': 'libatk-bridge2.0-0', 'libatspi.so.0': 'libatspi2.0-0', 'libcairo-gobject.so.2': 'libcairo-gobject2', 'libcairo.so.2': 'libcairo2', 'libcups.so.2': 'libcups2', 'libdbus-1.so.3': 'libdbus-1-3', 'libdbus-glib-1.so.2': 'libdbus-glib-1-2', 'libdrm.so.2': 'libdrm2', 'libEGL.so.1': 'libegl1', 'libenchant.so.1': 'libenchant1c2a', 'libepoxy.so.0': 'libepoxy0', 'libfontconfig.so.1': 'libfontconfig1', 'libfreetype.so.6': 'libfreetype6', 'libgbm.so.1': 'libgbm1', 'libgdk_pixbuf-2.0.so.0': 'libgdk-pixbuf2.0-0', 'libgdk-3.so.0': 'libgtk-3-0', 'libgdk-x11-2.0.so.0': 'libgtk2.0-0', 'libgio-2.0.so.0': 'libglib2.0-0', 'libGL.so.1': 'libgl1', 'libGLESv2.so.2': 'libgles2', 'libglib-2.0.so.0': 'libglib2.0-0', 'libgmodule-2.0.so.0': 'libglib2.0-0', 'libgobject-2.0.so.0': 'libglib2.0-0', 'libgstapp-1.0.so.0': 'libgstreamer-plugins-base1.0-0', 'libgstaudio-1.0.so.0': 'libgstreamer-plugins-base1.0-0', 'libgstbase-1.0.so.0': 'libgstreamer1.0-0', 'libgstcodecparsers-1.0.so.0': 'libgstreamer-plugins-bad1.0-0', 'libgstfft-1.0.so.0': 'libgstreamer-plugins-base1.0-0', 'libgstgl-1.0.so.0': 'libgstreamer-gl1.0-0', 'libgstpbutils-1.0.so.0': 'libgstreamer-plugins-base1.0-0', 'libgstreamer-1.0.so.0': 'libgstreamer1.0-0', 'libgsttag-1.0.so.0': 'libgstreamer-plugins-base1.0-0', 'libgstvideo-1.0.so.0': 'libgstreamer-plugins-base1.0-0', 'libgthread-2.0.so.0': 'libglib2.0-0', 'libgtk-3.so.0': 'libgtk-3-0', 'libgtk-x11-2.0.so.0': 'libgtk2.0-0', 'libharfbuzz-icu.so.0': 'libharfbuzz-icu0', 'libharfbuzz.so.0': 'libharfbuzz0b', 'libhyphen.so.0': 'libhyphen0', 'libicui18n.so.66': 'libicu66', 'libicuuc.so.66': 'libicu66', 'libjpeg.so.8': 'libjpeg-turbo8', 'libnotify.so.4': 'libnotify4', 'libnspr4.so': 'libnspr4', 'libnss3.so': 'libnss3', 'libnssutil3.so': 'libnss3', 'libopenjp2.so.7': 'libopenjp2-7', 'libopus.so.0': 'libopus0', 'libpango-1.0.so.0': 'libpango-1.0-0', 'libpangocairo-1.0.so.0': 'libpangocairo-1.0-0', 'libpangoft2-1.0.so.0': 'libpangoft2-1.0-0', 'libpng16.so.16': 'libpng16-16', 'libsecret-1.so.0': 'libsecret-1-0', 'libsmime3.so': 'libnss3', 'libsoup-2.4.so.1': 'libsoup2.4-1', 'libvpx.so.6': 'libvpx6', 'libwayland-client.so.0': 'libwayland-client0', 'libwayland-egl.so.1': 'libwayland-egl1', 'libwayland-server.so.0': 'libwayland-server0', 'libwebp.so.6': 'libwebp6', 'libwebpdemux.so.2': 'libwebpdemux2', 'libwoff2dec.so.1.0.2': 'libwoff1', 'libX11-xcb.so.1': 'libx11-xcb1', 'libX11.so.6': 'libx11-6', 'libxcb-dri3.so.0': 'libxcb-dri3-0', 'libxcb-shm.so.0': 'libxcb-shm0', 'libxcb.so.1': 'libxcb1', 'libXcomposite.so.1': 'libxcomposite1', 'libXcursor.so.1': 'libxcursor1', 'libXdamage.so.1': 'libxdamage1', 'libXext.so.6': 'libxext6', 'libXfixes.so.3': 'libxfixes3', 'libXi.so.6': 'libxi6', 'libxkbcommon.so.0': 'libxkbcommon0', 'libxml2.so.2': 'libxml2', 'libXrandr.so.2': 'libxrandr2', 'libXrender.so.1': 'libxrender1', 'libxslt.so.1': 'libxslt1.1', 'libXt.so.6': 'libxt6', 'libXtst.so.6': 'libxtst6', }; const MANUAL_LIBRARY_TO_PACKAGE_NAME_UBUNTU = { // libgstlibav.so (the only actual library provided by gstreamer1.0-libav) is not // in the ldconfig cache, so we detect the actual library required for playing h.264 // and if it's missing recommend installing missing gstreamer lib. // gstreamer1.0-libav -> libavcodec57 -> libx264-152 'libx264.so': 'gstreamer1.0-libav', }; //# sourceMappingURL=validateDependencies.js.map