Skip to content

Commit

Permalink
feat: support webkit technology preview
Browse files Browse the repository at this point in the history
This patch adds support for `technology preview` webkit channel, which
we will keep close to the actual Safari Technology Preview releases.

This channel does not install by default. It is supposed to be installed
with the following CLI command:

```sh
$ npx playwright install webkit-technology-preview
```

Once the channel is installed, it can be used the following way:

```js
const browser = await playwright.webkit.launch({
  channel: 'technology-preview',
});
```

**NOTE:** if clients attempt using the channel without installing it,
it'll throw an error with a copyable instructions to install via CLI.

References #5884
  • Loading branch information
aslushnikov committed Mar 19, 2021
1 parent 1f1c8b7 commit bc04a51
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 31 deletions.
6 changes: 6 additions & 0 deletions browsers.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
"mac10.14": "1443"
}
},
{
"name": "webkit-technology-preview",
"revision": "1443",
"download": true,
"installationSources": ["cli"]
},
{
"name": "ffmpeg",
"revision": "1005",
Expand Down
4 changes: 2 additions & 2 deletions packages/build_package.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
},
Expand All @@ -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': {
Expand Down
5 changes: 3 additions & 2 deletions src/cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/cli/driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
12 changes: 6 additions & 6 deletions src/install/installer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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');
Expand All @@ -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<string> = new Set();
for (const fileName of await fsReaddirAsync(linksDir)) {
Expand All @@ -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);
Expand Down Expand Up @@ -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}`);
Expand Down
11 changes: 6 additions & 5 deletions src/server/validateDependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
14 changes: 14 additions & 0 deletions src/server/webkit/webkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<WKBrowser> {
return WKBrowser.connect(transport, options);
}
Expand Down
66 changes: 51 additions & 15 deletions src/utils/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,21 @@ 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 = {
name: BrowserName,
revision: string,
download: boolean,
browserDirectory: string,
installationSources: Set<InstallationSource>,
};

const EXECUTABLE_PATHS = {
chromium: {
'chromium': {
'ubuntu18.04': ['chrome-linux', 'chrome'],
'ubuntu20.04': ['chrome-linux', 'chrome'],
'mac10.13': ['chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium'],
Expand All @@ -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'],
Expand All @@ -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,
Expand All @@ -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'],
Expand All @@ -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',
Expand All @@ -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',
Expand All @@ -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,
Expand All @@ -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',
Expand Down Expand Up @@ -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,
};
});
}
Expand Down Expand Up @@ -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') ||
Expand All @@ -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);
Expand Down

0 comments on commit bc04a51

Please sign in to comment.