chore: simplify bidi browsers handling (#36363)

This commit is contained in:
Dmitry Gozman 2025-06-20 10:00:21 +01:00 committed by GitHub
parent 173b455941
commit 1357f0ab83
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 41 additions and 76 deletions

View File

@ -32,9 +32,9 @@ import type { WebSocketEventEmitter } from './utilsBundle';
import type { Browser } from './server/browser'; import type { Browser } from './server/browser';
export class BrowserServerLauncherImpl implements BrowserServerLauncher { export class BrowserServerLauncherImpl implements BrowserServerLauncher {
private _browserName: 'chromium' | 'firefox' | 'webkit' | 'bidiFirefox' | 'bidiChromium'; private _browserName: 'chromium' | 'firefox' | 'webkit' | '_bidiFirefox' | '_bidiChromium';
constructor(browserName: 'chromium' | 'firefox' | 'webkit' | 'bidiFirefox' | 'bidiChromium') { constructor(browserName: 'chromium' | 'firefox' | 'webkit' | '_bidiFirefox' | '_bidiChromium') {
this._browserName = browserName; this._browserName = browserName;
} }

View File

@ -58,9 +58,9 @@ export class Playwright extends ChannelOwner<channels.PlaywrightChannel> {
this._android._playwright = this; this._android._playwright = this;
this._electron = Electron.from(initializer.electron); this._electron = Electron.from(initializer.electron);
this._electron._playwright = this; this._electron._playwright = this;
this._bidiChromium = BrowserType.from(initializer.bidiChromium); this._bidiChromium = BrowserType.from(initializer._bidiChromium);
this._bidiChromium._playwright = this; this._bidiChromium._playwright = this;
this._bidiFirefox = BrowserType.from(initializer.bidiFirefox); this._bidiFirefox = BrowserType.from(initializer._bidiFirefox);
this._bidiFirefox._playwright = this; this._bidiFirefox._playwright = this;
this.devices = this._connection.localUtils()?.devices ?? {}; this.devices = this._connection.localUtils()?.devices ?? {};
this.selectors = new Selectors(this._connection._platform); this.selectors = new Selectors(this._connection._platform);

View File

@ -42,8 +42,8 @@ export function createInProcessPlaywright(): PlaywrightAPI {
playwrightAPI.firefox._serverLauncher = new BrowserServerLauncherImpl('firefox'); playwrightAPI.firefox._serverLauncher = new BrowserServerLauncherImpl('firefox');
playwrightAPI.webkit._serverLauncher = new BrowserServerLauncherImpl('webkit'); playwrightAPI.webkit._serverLauncher = new BrowserServerLauncherImpl('webkit');
playwrightAPI._android._serverLauncher = new AndroidServerLauncherImpl(); playwrightAPI._android._serverLauncher = new AndroidServerLauncherImpl();
playwrightAPI._bidiChromium._serverLauncher = new BrowserServerLauncherImpl('bidiChromium'); playwrightAPI._bidiChromium._serverLauncher = new BrowserServerLauncherImpl('_bidiChromium');
playwrightAPI._bidiFirefox._serverLauncher = new BrowserServerLauncherImpl('bidiFirefox'); playwrightAPI._bidiFirefox._serverLauncher = new BrowserServerLauncherImpl('_bidiFirefox');
// Switch to async dispatch after we got Playwright object. // Switch to async dispatch after we got Playwright object.
dispatcherConnection.onmessage = message => setImmediate(() => clientConnection.dispatch(message)); dispatcherConnection.onmessage = message => setImmediate(() => clientConnection.dispatch(message));

View File

@ -376,8 +376,8 @@ scheme.PlaywrightInitializer = tObject({
chromium: tChannel(['BrowserType']), chromium: tChannel(['BrowserType']),
firefox: tChannel(['BrowserType']), firefox: tChannel(['BrowserType']),
webkit: tChannel(['BrowserType']), webkit: tChannel(['BrowserType']),
bidiChromium: tChannel(['BrowserType']), _bidiChromium: tChannel(['BrowserType']),
bidiFirefox: tChannel(['BrowserType']), _bidiFirefox: tChannel(['BrowserType']),
android: tChannel(['Android']), android: tChannel(['Android']),
electron: tChannel(['Electron']), electron: tChannel(['Electron']),
utils: tOptional(tChannel(['LocalUtils'])), utils: tOptional(tChannel(['LocalUtils'])),

View File

@ -118,14 +118,7 @@ export class PlaywrightConnection {
private async _initLaunchBrowserMode(scope: RootDispatcher, options: channels.RootInitializeParams) { private async _initLaunchBrowserMode(scope: RootDispatcher, options: channels.RootInitializeParams) {
debugLogger.log('server', `[${this._id}] engaged launch mode for "${this._options.browserName}"`); debugLogger.log('server', `[${this._id}] engaged launch mode for "${this._options.browserName}"`);
const ownedSocksProxy = await this._createOwnedSocksProxy(); const ownedSocksProxy = await this._createOwnedSocksProxy();
let browserName = this._options.browserName; const browser = await this._playwright[this._options.browserName as 'chromium'].launch(serverSideCallMetadata(), this._options.launchOptions);
if ('bidi' === browserName) {
if (this._options.launchOptions?.channel?.toLocaleLowerCase().includes('firefox'))
browserName = 'bidiFirefox';
else
browserName = 'bidiChromium';
}
const browser = await this._playwright[browserName as 'chromium'].launch(serverSideCallMetadata(), this._options.launchOptions);
browser.options.sdkLanguage = options.sdkLanguage; browser.options.sdkLanguage = options.sdkLanguage;
this._cleanups.push(() => browser.close({ reason: 'Connection terminated' })); this._cleanups.push(() => browser.close({ reason: 'Connection terminated' }));

View File

@ -34,7 +34,7 @@ import type * as types from '../types';
export class BidiChromium extends BrowserType { export class BidiChromium extends BrowserType {
constructor(parent: SdkObject) { constructor(parent: SdkObject) {
super(parent, 'bidi'); super(parent, '_bidiChromium');
} }
override async connectToTransport(transport: ConnectionTransport, options: BrowserOptions, browserLogsCollector: RecentLogsCollector): Promise<BidiBrowser> { override async connectToTransport(transport: ConnectionTransport, options: BrowserOptions, browserLogsCollector: RecentLogsCollector): Promise<BidiBrowser> {

View File

@ -35,7 +35,11 @@ import type { RecentLogsCollector } from '../utils/debugLogger';
export class BidiFirefox extends BrowserType { export class BidiFirefox extends BrowserType {
constructor(parent: SdkObject) { constructor(parent: SdkObject) {
super(parent, 'bidi'); super(parent, '_bidiFirefox');
}
override executablePath(): string {
return '';
} }
override async connectToTransport(transport: ConnectionTransport, options: BrowserOptions): Promise<BidiBrowser> { override async connectToTransport(transport: ConnectionTransport, options: BrowserOptions): Promise<BidiBrowser> {

View File

@ -52,15 +52,15 @@ export class PlaywrightDispatcher extends Dispatcher<Playwright, channels.Playwr
const chromium = new BrowserTypeDispatcher(scope, playwright.chromium, denyLaunch); const chromium = new BrowserTypeDispatcher(scope, playwright.chromium, denyLaunch);
const firefox = new BrowserTypeDispatcher(scope, playwright.firefox, denyLaunch); const firefox = new BrowserTypeDispatcher(scope, playwright.firefox, denyLaunch);
const webkit = new BrowserTypeDispatcher(scope, playwright.webkit, denyLaunch); const webkit = new BrowserTypeDispatcher(scope, playwright.webkit, denyLaunch);
const bidiChromium = new BrowserTypeDispatcher(scope, playwright.bidiChromium, denyLaunch); const _bidiChromium = new BrowserTypeDispatcher(scope, playwright._bidiChromium, denyLaunch);
const bidiFirefox = new BrowserTypeDispatcher(scope, playwright.bidiFirefox, denyLaunch); const _bidiFirefox = new BrowserTypeDispatcher(scope, playwright._bidiFirefox, denyLaunch);
const android = new AndroidDispatcher(scope, playwright.android, denyLaunch); const android = new AndroidDispatcher(scope, playwright.android, denyLaunch);
const initializer: channels.PlaywrightInitializer = { const initializer: channels.PlaywrightInitializer = {
chromium, chromium,
firefox, firefox,
webkit, webkit,
bidiChromium, _bidiChromium,
bidiFirefox, _bidiFirefox,
android, android,
electron: new ElectronDispatcher(scope, playwright.electron, denyLaunch), electron: new ElectronDispatcher(scope, playwright.electron, denyLaunch),
utils: playwright.options.isServer ? undefined : new LocalUtilsDispatcher(scope, playwright), utils: playwright.options.isServer ? undefined : new LocalUtilsDispatcher(scope, playwright),
@ -69,14 +69,7 @@ export class PlaywrightDispatcher extends Dispatcher<Playwright, channels.Playwr
let browserDispatcher: BrowserDispatcher | undefined; let browserDispatcher: BrowserDispatcher | undefined;
if (options.preLaunchedBrowser) { if (options.preLaunchedBrowser) {
let browserTypeDispatcher: BrowserTypeDispatcher; const browserTypeDispatcher = initializer[options.preLaunchedBrowser.options.name as keyof typeof initializer] as BrowserTypeDispatcher;
switch (options.preLaunchedBrowser.options.name) {
case 'chromium': browserTypeDispatcher = chromium; break;
case 'firefox': browserTypeDispatcher = firefox; break;
case 'webkit': browserTypeDispatcher = webkit; break;
case 'bidi': browserTypeDispatcher = options.preLaunchedBrowser.options.channel?.includes('firefox') ? bidiFirefox : bidiChromium; break;
default: throw new Error(`Unknown browser name: ${options.preLaunchedBrowser.options.name}`);
}
browserDispatcher = new BrowserDispatcher(browserTypeDispatcher, options.preLaunchedBrowser, { browserDispatcher = new BrowserDispatcher(browserTypeDispatcher, options.preLaunchedBrowser, {
ignoreStopAndKill: true, ignoreStopAndKill: true,
isolateContexts: !options.sharedBrowser, isolateContexts: !options.sharedBrowser,

View File

@ -44,8 +44,8 @@ export class Playwright extends SdkObject {
readonly electron: Electron; readonly electron: Electron;
readonly firefox: BrowserType; readonly firefox: BrowserType;
readonly webkit: BrowserType; readonly webkit: BrowserType;
readonly bidiChromium: BrowserType; readonly _bidiChromium: BrowserType;
readonly bidiFirefox: BrowserType; readonly _bidiFirefox: BrowserType;
readonly options: PlaywrightOptions; readonly options: PlaywrightOptions;
readonly debugController: DebugController; readonly debugController: DebugController;
private _allPages = new Set<Page>(); private _allPages = new Set<Page>();
@ -65,8 +65,8 @@ export class Playwright extends SdkObject {
} }
}, null); }, null);
this.chromium = new Chromium(this); this.chromium = new Chromium(this);
this.bidiChromium = new BidiChromium(this); this._bidiChromium = new BidiChromium(this);
this.bidiFirefox = new BidiFirefox(this); this._bidiFirefox = new BidiFirefox(this);
this.firefox = new Firefox(this); this.firefox = new Firefox(this);
this.webkit = new WebKit(this); this.webkit = new WebKit(this);
this.electron = new Electron(this); this.electron = new Electron(this);

View File

@ -384,7 +384,9 @@ const DOWNLOAD_PATHS: Record<BrowserName | InternalTool, DownloadPaths> = {
'win64': 'builds/android/%s/android.zip', 'win64': 'builds/android/%s/android.zip',
}, },
// TODO(bidi): implement downloads. // TODO(bidi): implement downloads.
'bidi': { '_bidiFirefox': {
} as DownloadPaths,
'_bidiChromium': {
} as DownloadPaths, } as DownloadPaths,
}; };
@ -480,7 +482,7 @@ function readDescriptors(browsersJSON: BrowsersJSON): BrowsersJSONDescriptor[] {
}); });
} }
export type BrowserName = 'chromium' | 'firefox' | 'webkit' | 'bidi'; export type BrowserName = 'chromium' | 'firefox' | 'webkit' | '_bidiFirefox' | '_bidiChromium';
type InternalTool = 'ffmpeg' | 'winldd' | 'firefox-beta' | 'chromium-tip-of-tree' | 'chromium-headless-shell' | 'chromium-tip-of-tree-headless-shell' | 'android'; type InternalTool = 'ffmpeg' | 'winldd' | 'firefox-beta' | 'chromium-tip-of-tree' | 'chromium-headless-shell' | 'chromium-tip-of-tree-headless-shell' | 'android';
type BidiChannel = 'moz-firefox' | 'moz-firefox-beta' | 'moz-firefox-nightly' | 'bidi-chrome-canary' | 'bidi-chrome-stable' | 'bidi-chromium'; type BidiChannel = 'moz-firefox' | 'moz-firefox-beta' | 'moz-firefox-nightly' | 'bidi-chrome-canary' | 'bidi-chrome-stable' | 'bidi-chromium';
type ChromiumChannel = 'chrome' | 'chrome-beta' | 'chrome-dev' | 'chrome-canary' | 'msedge' | 'msedge-beta' | 'msedge-dev' | 'msedge-canary'; type ChromiumChannel = 'chrome' | 'chrome-beta' | 'chrome-dev' | 'chrome-canary' | 'msedge' | 'msedge-beta' | 'msedge-dev' | 'msedge-canary';
@ -717,8 +719,8 @@ export class Registry {
})); }));
this._executables.push({ this._executables.push({
type: 'browser', type: 'browser',
name: 'bidi-chromium', name: '_bidiChromium',
browserName: 'bidi', browserName: '_bidiChromium',
directory: chromium.dir, directory: chromium.dir,
executablePath: () => chromiumExecutable, executablePath: () => chromiumExecutable,
executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('chromium', chromiumExecutable, chromium.installByDefault, sdkLanguage), executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('chromium', chromiumExecutable, chromium.installByDefault, sdkLanguage),
@ -842,21 +844,6 @@ export class Registry {
_dependencyGroup: 'tools', _dependencyGroup: 'tools',
_isHermeticInstallation: true, _isHermeticInstallation: true,
}); });
this._executables.push({
type: 'browser',
name: 'bidi',
browserName: 'bidi',
directory: undefined,
executablePath: () => undefined,
executablePathOrDie: () => '',
installType: 'none',
_validateHostRequirements: () => Promise.resolve(),
downloadURLs: [],
_install: () => Promise.resolve(),
_dependencyGroup: 'tools',
_isHermeticInstallation: true,
});
} }
private _createChromiumChannel(name: ChromiumChannel, lookAt: Record<'linux' | 'darwin' | 'win32', string>, install?: () => Promise<void>): ExecutableImpl { private _createChromiumChannel(name: ChromiumChannel, lookAt: Record<'linux' | 'darwin' | 'win32', string>, install?: () => Promise<void>): ExecutableImpl {
@ -931,7 +918,7 @@ export class Registry {
return { return {
type: 'channel', type: 'channel',
name, name,
browserName: 'bidi', browserName: '_bidiFirefox',
directory: undefined, directory: undefined,
executablePath: (sdkLanguage: string) => executablePath(sdkLanguage, false), executablePath: (sdkLanguage: string) => executablePath(sdkLanguage, false),
executablePathOrDie: (sdkLanguage: string) => executablePath(sdkLanguage, true)!, executablePathOrDie: (sdkLanguage: string) => executablePath(sdkLanguage, true)!,
@ -947,7 +934,7 @@ export class Registry {
const suffix = lookAt[process.platform as 'linux' | 'darwin' | 'win32']; const suffix = lookAt[process.platform as 'linux' | 'darwin' | 'win32'];
if (!suffix) { if (!suffix) {
if (shouldThrow) if (shouldThrow)
throw new Error(`Firefox distribution '${name}' is not supported on ${process.platform}`); throw new Error(`Chromium distribution '${name}' is not supported on ${process.platform}`);
return undefined; return undefined;
} }
const prefixes = (process.platform === 'win32' ? [ const prefixes = (process.platform === 'win32' ? [
@ -974,7 +961,7 @@ export class Registry {
return { return {
type: 'channel', type: 'channel',
name, name,
browserName: 'bidi', browserName: '_bidiChromium',
directory: undefined, directory: undefined,
executablePath: (sdkLanguage: string) => executablePath(sdkLanguage, false), executablePath: (sdkLanguage: string) => executablePath(sdkLanguage, false),
executablePathOrDie: (sdkLanguage: string) => executablePath(sdkLanguage, true)!, executablePathOrDie: (sdkLanguage: string) => executablePath(sdkLanguage, true)!,

View File

@ -624,8 +624,8 @@ export type PlaywrightInitializer = {
chromium: BrowserTypeChannel, chromium: BrowserTypeChannel,
firefox: BrowserTypeChannel, firefox: BrowserTypeChannel,
webkit: BrowserTypeChannel, webkit: BrowserTypeChannel,
bidiChromium: BrowserTypeChannel, _bidiChromium: BrowserTypeChannel,
bidiFirefox: BrowserTypeChannel, _bidiFirefox: BrowserTypeChannel,
android: AndroidChannel, android: AndroidChannel,
electron: ElectronChannel, electron: ElectronChannel,
utils?: LocalUtilsChannel, utils?: LocalUtilsChannel,

View File

@ -791,8 +791,8 @@ Playwright:
chromium: BrowserType chromium: BrowserType
firefox: BrowserType firefox: BrowserType
webkit: BrowserType webkit: BrowserType
bidiChromium: BrowserType _bidiChromium: BrowserType
bidiFirefox: BrowserType _bidiFirefox: BrowserType
android: Android android: Android
electron: Electron electron: Electron
utils: LocalUtils? utils: LocalUtils?

View File

@ -106,7 +106,7 @@ for (const [key, channels] of Object.entries(browserToChannels)) {
use: { use: {
browserName, browserName,
headless: !headed, headless: !headed,
channel, channel: channel === 'bidi-chromium' ? undefined : channel,
video: 'off', video: 'off',
launchOptions: { launchOptions: {
executablePath, executablePath,

View File

@ -103,12 +103,6 @@ export class RemoteServer implements PlaywrightServer {
launchOptions, launchOptions,
...remoteServerOptions, ...remoteServerOptions,
}; };
if ('bidi' === browserType.name()) {
if (channel.toLocaleLowerCase().includes('firefox'))
options.browserTypeName = '_bidiFirefox';
else
options.browserTypeName = '_bidiChromium';
}
this._process = childProcess({ this._process = childProcess({
command: ['node', path.join(__dirname, 'remote-server-impl.js'), JSON.stringify(options)], command: ['node', path.join(__dirname, 'remote-server-impl.js'), JSON.stringify(options)],
}); });

View File

@ -258,9 +258,7 @@ for (const kind of ['launchServer', 'run-server'] as const) {
}).catch(() => {}) }).catch(() => {})
]); ]);
expect(request.headers['user-agent']).toBe(getUserAgent()); expect(request.headers['user-agent']).toBe(getUserAgent());
// _bidiFirefox and _bidiChromium are initialized with 'bidi' as browser name. expect(request.headers['x-playwright-browser']).toBe(browserName);
const bidiAwareBrowserName = browserName.startsWith('_bidi') ? 'bidi' : browserName;
expect(request.headers['x-playwright-browser']).toBe(bidiAwareBrowserName);
expect(request.headers['foo']).toBe('bar'); expect(request.headers['foo']).toBe('bar');
}); });

View File

@ -56,9 +56,7 @@ it('should have browser', async ({ browserName, browser, contextFactory, server
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
const log = await getLog(); const log = await getLog();
// _bidiFirefox and _bidiChromium are initialized with 'bidi' as browser name. expect(log.browser!.name.toLowerCase()).toBe(browserName);
const harBrowserName = browserName.startsWith('_bidi') ? 'bidi' : browserName;
expect(log.browser!.name.toLowerCase()).toBe(harBrowserName);
expect(log.browser!.version).toBe(browser.version()); expect(log.browser!.version).toBe(browser.version());
}); });
@ -915,8 +913,6 @@ it('should not hang on slow chunked response', async ({ browserName, browser, co
await page.evaluate(() => (window as any).receivedFirstData); await page.evaluate(() => (window as any).receivedFirstData);
const log = await getLog(); const log = await getLog();
// _bidiFirefox and _bidiChromium are initialized with 'bidi' as browser name. expect(log.browser!.name.toLowerCase()).toBe(browserName);
const harBrowserName = browserName.startsWith('_bidi') ? 'bidi' : browserName;
expect(log.browser!.name.toLowerCase()).toBe(harBrowserName);
expect(log.browser!.version).toBe(browser.version()); expect(log.browser!.version).toBe(browser.version());
}); });