diff --git a/browsers.json b/browsers.json index d3776837ee2052..7a4a640c41e7d0 100644 --- a/browsers.json +++ b/browsers.json @@ -19,6 +19,12 @@ "mac10.14": "1443" } }, + { + "name": "webkit-technology-preview", + "revision": "1443", + "download": true, + "installationSources": ["cli"] + }, { "name": "ffmpeg", "revision": "1005", diff --git a/packages/build_package.js b/packages/build_package.js index 5923e12179102f..82f6a509780963 100755 --- a/packages/build_package.js +++ b/packages/build_package.js @@ -33,7 +33,7 @@ const PLAYWRIGHT_CORE_FILES = ['bin/PrintDeps.exe', 'lib', 'types', 'NOTICE', 'L const PACKAGES = { 'playwright': { description: 'A high-level API to automate web browsers', - browsers: ['chromium', 'firefox', 'webkit', 'ffmpeg'], + browsers: ['chromium', 'firefox', 'webkit', 'ffmpeg', 'webkit-technology-preview'], // We copy README.md additionally for Playwright so that it looks nice on NPM. files: [...PLAYWRIGHT_CORE_FILES, 'README.md'], }, @@ -44,7 +44,7 @@ const PACKAGES = { }, 'playwright-webkit': { description: 'A high-level API to automate WebKit', - browsers: ['webkit'], + browsers: ['webkit', 'webkit-technology-preview'], files: PLAYWRIGHT_CORE_FILES, }, 'playwright-firefox': { diff --git a/src/cli/cli.ts b/src/cli/cli.ts index 2476e1f35c9af5..3a825b924ba7b9 100755 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -32,6 +32,7 @@ import { BrowserType } from '../client/browserType'; import { BrowserContextOptions, LaunchOptions } from '../client/types'; import { spawn } from 'child_process'; import { installDeps } from '../install/installDeps'; +import { allBrowserNames } from '../utils/registry'; program .version('Version ' + require('../../package.json').version) @@ -85,10 +86,10 @@ program .description('ensure browsers necessary for this version of Playwright are installed') .action(async function(browserType) { try { - const allBrowsers = new Set(['chromium', 'firefox', 'webkit', 'ffmpeg']); + const allBrowsers = new Set(allBrowserNames); for (const type of browserType) { if (!allBrowsers.has(type)) { - console.log(`Invalid browser name: '${type}'. Expecting 'chromium', 'firefox' or 'webkit'.`); + console.log(`Invalid browser name: '${type}'. Expecting one of: ${allBrowserNames.map(name => `'${name}'`).join(', ')}`); process.exit(1); } } diff --git a/src/cli/driver.ts b/src/cli/driver.ts index 55b6d44cbed9bc..249f14db666042 100644 --- a/src/cli/driver.ts +++ b/src/cli/driver.ts @@ -66,5 +66,5 @@ export async function launchBrowserServer(browserName: string, configFile?: stri } export async function installBrowsers(browserNames?: BrowserName[]) { - await installBrowsersWithProgressBar(browserNames); + await installBrowsersWithProgressBar(browserNames, 'cli'); } diff --git a/src/install/installer.ts b/src/install/installer.ts index 186c01c1f13601..683d96cd327bae 100644 --- a/src/install/installer.ts +++ b/src/install/installer.ts @@ -18,7 +18,7 @@ import fs from 'fs'; import path from 'path'; import util from 'util'; import lockfile from 'proper-lockfile'; -import {Registry, allBrowserNames, isBrowserDirectory, BrowserName, registryDirectory} from '../utils/registry'; +import {Registry, allBrowserNames, isBrowserDirectory, BrowserName, registryDirectory, InstallationSource} from '../utils/registry'; import * as browserFetcher from './browserFetcher'; import { getAsBooleanFromENV, calculateSha1, removeFolders } from '../utils/utils'; @@ -31,7 +31,7 @@ const fsWriteFileAsync = util.promisify(fs.writeFile.bind(fs)); const PACKAGE_PATH = path.join(__dirname, '..', '..'); -export async function installBrowsersWithProgressBar(browserNames: BrowserName[] = allBrowserNames) { +export async function installBrowsersWithProgressBar(browserNames: BrowserName[] = allBrowserNames, installationSource : InstallationSource = 'default') { // PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD should have a value of 0 or 1 if (getAsBooleanFromENV('PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD')) { browserFetcher.logPolitely('Skipping browsers download because `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD` env variable is set'); @@ -58,13 +58,13 @@ export async function installBrowsersWithProgressBar(browserNames: BrowserName[] try { await fsMkdirAsync(linksDir, { recursive: true }); await fsWriteFileAsync(path.join(linksDir, calculateSha1(PACKAGE_PATH)), PACKAGE_PATH); - await validateCache(linksDir, browserNames); + await validateCache(linksDir, browserNames, installationSource); } finally { await releaseLock(); } } -async function validateCache(linksDir: string, browserNames: BrowserName[]) { +async function validateCache(linksDir: string, browserNames: BrowserName[], installationSource: InstallationSource) { // 1. Collect used downloads and package descriptors. const usedBrowserPaths: Set = new Set(); for (const fileName of await fsReaddirAsync(linksDir)) { @@ -74,7 +74,7 @@ async function validateCache(linksDir: string, browserNames: BrowserName[]) { linkTarget = (await fsReadFileAsync(linkPath)).toString(); const linkRegistry = new Registry(linkTarget); for (const browserName of allBrowserNames) { - if (!linkRegistry.shouldDownload(browserName)) + if (!linkRegistry.shouldRetain(browserName)) continue; const usedBrowserPath = linkRegistry.browserDirectory(browserName); const browserRevision = linkRegistry.revision(browserName); @@ -107,7 +107,7 @@ async function validateCache(linksDir: string, browserNames: BrowserName[]) { // 3. Install missing browsers for this package. const myRegistry = new Registry(PACKAGE_PATH); for (const browserName of browserNames) { - if (!myRegistry.shouldDownload(browserName)) + if (!myRegistry.shouldDownload(browserName, installationSource)) continue; await browserFetcher.downloadBrowserWithProgressBar(myRegistry, browserName).catch(e => { throw new Error(`Failed to download ${browserName}, caused by\n${e.stack}`); diff --git a/src/server/validateDependencies.ts b/src/server/validateDependencies.ts index 22281d2172f958..c68d94aa728cdc 100644 --- a/src/server/validateDependencies.ts +++ b/src/server/validateDependencies.ts @@ -43,11 +43,12 @@ export async function validateHostRequirements(registry: registry.Registry, brow } const DL_OPEN_LIBRARIES = { - chromium: [], - webkit: ['libGLESv2.so.2', 'libx264.so'], - firefox: [], - clank: [], - ffmpeg: [], + 'chromium': [], + 'webkit': ['libGLESv2.so.2', 'libx264.so'], + 'webkit-technology-preview': ['libGLESv2.so.2', 'libx264.so'], + 'firefox': [], + 'clank': [], + 'ffmpeg': [], }; function isSupportedWindowsVersion(): boolean { diff --git a/src/server/webkit/webkit.ts b/src/server/webkit/webkit.ts index dbf7bc13f1037d..21d6ecba4afb23 100644 --- a/src/server/webkit/webkit.ts +++ b/src/server/webkit/webkit.ts @@ -23,12 +23,26 @@ import { BrowserType } from '../browserType'; import { ConnectionTransport } from '../transport'; import { BrowserOptions, PlaywrightOptions } from '../browser'; import * as types from '../types'; +import * as fs from 'fs'; +import { assert } from '../../utils/utils'; export class WebKit extends BrowserType { constructor(playwrightOptions: PlaywrightOptions) { super('webkit', playwrightOptions); } + executablePath(options?: types.LaunchOptions): string { + if (options?.channel) { + let executablePath = undefined; + if (options.channel === 'technology-preview') + executablePath = this._registry.executablePath('webkit-technology-preview'); + assert(executablePath, `unsupported webkit channel "${options.channel}"`); + assert(fs.existsSync(executablePath), `webkit channel "${options.channel}" is not installed. Try running 'npx playwright install webkit-technology-preview'`); + return executablePath; + } + return super.executablePath(options); + } + _connectToTransport(transport: ConnectionTransport, options: BrowserOptions): Promise { return WKBrowser.connect(transport, options); } diff --git a/src/utils/registry.ts b/src/utils/registry.ts index 8590784a8a5805..e0c7fd20a42b96 100644 --- a/src/utils/registry.ts +++ b/src/utils/registry.ts @@ -23,8 +23,9 @@ import * as util from 'util'; import { getUbuntuVersionSync } from './ubuntuVersion'; import { assert, getFromENV } from './utils'; -export type BrowserName = 'chromium'|'webkit'|'firefox'|'ffmpeg'; -export const allBrowserNames: BrowserName[] = ['chromium', 'webkit', 'firefox', 'ffmpeg']; +export type InstallationSource = 'default'|'cli'; +export type BrowserName = 'chromium'|'webkit'|'webkit-technology-preview'|'firefox'|'ffmpeg'; +export const allBrowserNames: BrowserName[] = ['chromium', 'webkit', 'webkit-technology-preview', 'firefox', 'ffmpeg']; type BrowserPlatform = 'win32'|'win64'|'mac10.13'|'mac10.14'|'mac10.15'|'mac11'|'mac11-arm64'|'ubuntu18.04'|'ubuntu20.04'; type BrowserDescriptor = { @@ -32,10 +33,11 @@ type BrowserDescriptor = { revision: string, download: boolean, browserDirectory: string, + installationSources: Set, }; const EXECUTABLE_PATHS = { - chromium: { + 'chromium': { 'ubuntu18.04': ['chrome-linux', 'chrome'], 'ubuntu20.04': ['chrome-linux', 'chrome'], 'mac10.13': ['chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium'], @@ -46,7 +48,7 @@ const EXECUTABLE_PATHS = { 'win32': ['chrome-win', 'chrome.exe'], 'win64': ['chrome-win', 'chrome.exe'], }, - firefox: { + 'firefox': { 'ubuntu18.04': ['firefox', 'firefox'], 'ubuntu20.04': ['firefox', 'firefox'], 'mac10.13': ['firefox', 'Nightly.app', 'Contents', 'MacOS', 'firefox'], @@ -57,7 +59,7 @@ const EXECUTABLE_PATHS = { 'win32': ['firefox', 'firefox.exe'], 'win64': ['firefox', 'firefox.exe'], }, - webkit: { + 'webkit': { 'ubuntu18.04': ['pw_run.sh'], 'ubuntu20.04': ['pw_run.sh'], 'mac10.13': undefined, @@ -68,7 +70,18 @@ const EXECUTABLE_PATHS = { 'win32': ['Playwright.exe'], 'win64': ['Playwright.exe'], }, - ffmpeg: { + 'webkit-technology-preview': { + 'ubuntu18.04': ['pw_run.sh'], + 'ubuntu20.04': ['pw_run.sh'], + 'mac10.13': undefined, + 'mac10.14': ['pw_run.sh'], + 'mac10.15': ['pw_run.sh'], + 'mac11': ['pw_run.sh'], + 'mac11-arm64': ['pw_run.sh'], + 'win32': ['Playwright.exe'], + 'win64': ['Playwright.exe'], + }, + 'ffmpeg': { 'ubuntu18.04': ['ffmpeg-linux'], 'ubuntu20.04': ['ffmpeg-linux'], 'mac10.13': ['ffmpeg-mac'], @@ -82,7 +95,7 @@ const EXECUTABLE_PATHS = { }; const DOWNLOAD_URLS = { - chromium: { + 'chromium': { 'ubuntu18.04': '%s/builds/chromium/%s/chromium-linux.zip', 'ubuntu20.04': '%s/builds/chromium/%s/chromium-linux.zip', 'mac10.13': '%s/builds/chromium/%s/chromium-mac.zip', @@ -93,7 +106,7 @@ const DOWNLOAD_URLS = { 'win32': '%s/builds/chromium/%s/chromium-win32.zip', 'win64': '%s/builds/chromium/%s/chromium-win64.zip', }, - firefox: { + 'firefox': { 'ubuntu18.04': '%s/builds/firefox/%s/firefox-ubuntu-18.04.zip', 'ubuntu20.04': '%s/builds/firefox/%s/firefox-ubuntu-18.04.zip', 'mac10.13': '%s/builds/firefox/%s/firefox-mac-10.14.zip', @@ -104,7 +117,7 @@ const DOWNLOAD_URLS = { 'win32': '%s/builds/firefox/%s/firefox-win32.zip', 'win64': '%s/builds/firefox/%s/firefox-win64.zip', }, - webkit: { + 'webkit': { 'ubuntu18.04': '%s/builds/webkit/%s/webkit-ubuntu-18.04.zip', 'ubuntu20.04': '%s/builds/webkit/%s/webkit-ubuntu-20.04.zip', 'mac10.13': undefined, @@ -115,7 +128,18 @@ const DOWNLOAD_URLS = { 'win32': '%s/builds/webkit/%s/webkit-win64.zip', 'win64': '%s/builds/webkit/%s/webkit-win64.zip', }, - ffmpeg: { + 'webkit-technology-preview': { + 'ubuntu18.04': '%s/builds/webkit/%s/webkit-ubuntu-18.04.zip', + 'ubuntu20.04': '%s/builds/webkit/%s/webkit-ubuntu-20.04.zip', + 'mac10.13': undefined, + 'mac10.14': undefined, + 'mac10.15': '%s/builds/webkit/%s/webkit-mac-10.15.zip', + 'mac11': '%s/builds/webkit/%s/webkit-mac-10.15.zip', + 'mac11-arm64': '%s/builds/webkit/%s/webkit-mac-11.0-arm64.zip', + 'win32': '%s/builds/webkit/%s/webkit-win64.zip', + 'win64': '%s/builds/webkit/%s/webkit-win64.zip', + }, + 'ffmpeg': { 'ubuntu18.04': '%s/builds/ffmpeg/%s/ffmpeg-linux.zip', 'ubuntu20.04': '%s/builds/ffmpeg/%s/ffmpeg-linux.zip', 'mac10.13': '%s/builds/ffmpeg/%s/ffmpeg-mac.zip', @@ -209,11 +233,13 @@ export class Registry { const revisionOverride = (obj.revisionOverrides || {})[hostPlatform]; const revision = revisionOverride || obj.revision; const browserDirectory = revisionOverride ? `${name}-${hostPlatform}-special-${revision}` : `${name}-${revision}`; + const installationSources = new Set(obj.installationSources || ['default']); return { name, revision, download: obj.download, browserDirectory, + installationSources, }; }); } @@ -270,10 +296,11 @@ export class Registry { const browser = this._descriptors.find(browser => browser.name === browserName); assert(browser, `ERROR: Playwright does not support ${browserName}`); const envDownloadHost: { [key: string]: string } = { - chromium: 'PLAYWRIGHT_CHROMIUM_DOWNLOAD_HOST', - firefox: 'PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST', - webkit: 'PLAYWRIGHT_WEBKIT_DOWNLOAD_HOST', - ffmpeg: 'PLAYWRIGHT_FFMPEG_DOWNLOAD_HOST', + 'chromium': 'PLAYWRIGHT_CHROMIUM_DOWNLOAD_HOST', + 'firefox': 'PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST', + 'webkit': 'PLAYWRIGHT_WEBKIT_DOWNLOAD_HOST', + 'webkit-technology-preview': 'PLAYWRIGHT_WEBKIT_DOWNLOAD_HOST', + 'ffmpeg': 'PLAYWRIGHT_FFMPEG_DOWNLOAD_HOST', }; const downloadHost = getFromENV(envDownloadHost[browserName]) || getFromENV('PLAYWRIGHT_DOWNLOAD_HOST') || @@ -283,7 +310,16 @@ export class Registry { return util.format(urlTemplate, downloadHost, browser.revision); } - shouldDownload(browserName: BrowserName): boolean { + shouldDownload(browserName: BrowserName, installationSource: InstallationSource): boolean { + // Older versions do not have "download" field. We assume they need all browsers + // from the list. So we want to skip all browsers that are explicitly marked as "download: false". + const browser = this._descriptors.find(browser => browser.name === browserName); + if (!browser || browser.download === false) + return false; + return browser.installationSources.has(installationSource); + } + + shouldRetain(browserName: BrowserName): boolean { // Older versions do not have "download" field. We assume they need all browsers // from the list. So we want to skip all browsers that are explicitly marked as "download: false". const browser = this._descriptors.find(browser => browser.name === browserName);