Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions packages/playwright-core/src/protocol/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,6 @@ scheme.BrowserTypeLaunchParams = tObject({
args: tOptional(tArray(tString)),
ignoreAllDefaultArgs: tOptional(tBoolean),
ignoreDefaultArgs: tOptional(tArray(tString)),
assistantMode: tOptional(tBoolean),
handleSIGINT: tOptional(tBoolean),
handleSIGTERM: tOptional(tBoolean),
handleSIGHUP: tOptional(tBoolean),
Expand Down Expand Up @@ -541,7 +540,6 @@ scheme.BrowserTypeLaunchPersistentContextParams = tObject({
args: tOptional(tArray(tString)),
ignoreAllDefaultArgs: tOptional(tBoolean),
ignoreDefaultArgs: tOptional(tArray(tString)),
assistantMode: tOptional(tBoolean),
handleSIGINT: tOptional(tBoolean),
handleSIGTERM: tOptional(tBoolean),
handleSIGHUP: tOptional(tBoolean),
Expand Down
2 changes: 1 addition & 1 deletion packages/playwright-core/src/server/android/android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ export class AndroidDevice extends SdkObject {
'--disable-fre',
'--no-default-browser-check',
`--remote-debugging-socket-name=${socketName}`,
...chromiumSwitches(undefined, undefined, true),
...chromiumSwitches({ android: true }),
...this._innerDefaultArgs(options)
];
return chromeArguments;
Expand Down
4 changes: 2 additions & 2 deletions packages/playwright-core/src/server/bidi/bidiChromium.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export class BidiChromium extends BrowserType {
}

override async waitForReadyState(options: types.LaunchOptions, browserLogsCollector: RecentLogsCollector): Promise<{ wsEndpoint?: string }> {
return waitForReadyState({ ...options, cdpPort: 0 }, browserLogsCollector);
return waitForReadyState({ ...options, args: ['--remote-debugging-port=0'] }, browserLogsCollector);
}

private _innerDefaultArgs(options: types.LaunchOptions): string[] {
Expand All @@ -114,7 +114,7 @@ export class BidiChromium extends BrowserType {
throw new Error('Playwright manages remote debugging connection itself.');
if (args.find(arg => !arg.startsWith('-')))
throw new Error('Arguments can not specify page to be opened');
const chromeArguments = [...chromiumSwitches(options.assistantMode)];
const chromeArguments = [...chromiumSwitches()];

if (os.platform() === 'darwin') {
// See https://issues.chromium.org/issues/40277080
Expand Down
2 changes: 1 addition & 1 deletion packages/playwright-core/src/server/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export type BrowserOptions = {
protocolLogger: types.ProtocolLogger,
browserLogsCollector: RecentLogsCollector,
slowMo?: number;
wsEndpoint?: string; // Only there when connected over web socket.
wsEndpoint?: string;
sdkLanguage?: Language;
originalLaunchOptions: types.LaunchOptions;
userDataDir?: string;
Expand Down
12 changes: 6 additions & 6 deletions packages/playwright-core/src/server/browserType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export abstract class BrowserType extends SdkObject {
return this._innerLaunchWithRetries(progress, options, undefined, helper.debugProtocolLogger(protocolLogger)).catch(e => { throw this._rewriteStartupLog(e); });
}

async launchPersistentContext(progress: Progress, userDataDir: string, options: channels.BrowserTypeLaunchPersistentContextOptions & { cdpPort?: number, internalIgnoreHTTPSErrors?: boolean, socksProxyPort?: number }): Promise<BrowserContext> {
async launchPersistentContext(progress: Progress, userDataDir: string, options: channels.BrowserTypeLaunchPersistentContextOptions & { internalIgnoreHTTPSErrors?: boolean, socksProxyPort?: number }): Promise<BrowserContext> {
const launchOptions = this._validateLaunchOptions(options);
// Note: Any initial TLS requests will fail since we rely on the Page/Frames initialize which sets ignoreHTTPSErrors.
let clientCertificatesProxy: ClientCertificatesProxy | undefined;
Expand Down Expand Up @@ -109,7 +109,7 @@ export abstract class BrowserType extends SdkObject {
private async _innerLaunch(progress: Progress, options: types.LaunchOptions, persistent: types.BrowserContextOptions | undefined, protocolLogger: types.ProtocolLogger, maybeUserDataDir?: string): Promise<Browser> {
options.proxy = options.proxy ? normalizeProxySettings(options.proxy) : undefined;
const browserLogsCollector = new RecentLogsCollector();
const { browserProcess, userDataDir, artifactsDir, transport } = await this._launchProcess(progress, options, !!persistent, browserLogsCollector, maybeUserDataDir);
const { browserProcess, userDataDir, artifactsDir, transport, wsEndpoint } = await this._launchProcess(progress, options, !!persistent, browserLogsCollector, maybeUserDataDir);
try {
if ((options as any).__testHookBeforeCreateBrowser)
await progress.race((options as any).__testHookBeforeCreateBrowser());
Expand All @@ -128,7 +128,7 @@ export abstract class BrowserType extends SdkObject {
proxy: options.proxy,
protocolLogger,
browserLogsCollector,
wsEndpoint: transport instanceof WebSocketTransport ? transport.wsEndpoint : undefined,
wsEndpoint,
originalLaunchOptions: options,
userDataDir: persistent ? userDataDir : undefined,
};
Expand Down Expand Up @@ -198,7 +198,7 @@ export abstract class BrowserType extends SdkObject {
return { executable, browserArguments, userDataDir, artifactsDir, tempDirectories };
}

private async _launchProcess(progress: Progress, options: types.LaunchOptions, isPersistent: boolean, browserLogsCollector: RecentLogsCollector, userDataDir?: string): Promise<{ browserProcess: BrowserProcess, artifactsDir: string, userDataDir: string, transport: ConnectionTransport }> {
private async _launchProcess(progress: Progress, options: types.LaunchOptions, isPersistent: boolean, browserLogsCollector: RecentLogsCollector, userDataDir?: string): Promise<{ browserProcess: BrowserProcess, artifactsDir: string, userDataDir: string, transport: ConnectionTransport, wsEndpoint?: string }> {
const {
handleSIGINT = true,
handleSIGTERM = true,
Expand Down Expand Up @@ -273,13 +273,13 @@ export abstract class BrowserType extends SdkObject {
const updatedLog = this.doRewriteStartupLog(log);
throw new Error(`Failed to launch the browser process.\nBrowser logs:\n${updatedLog}`);
}
if (options.cdpPort !== undefined || !this.supportsPipeTransport()) {
if (!this.supportsPipeTransport()) {
transport = await WebSocketTransport.connect(progress, wsEndpoint!);
} else {
const stdio = launchedProcess.stdio as unknown as [NodeJS.ReadableStream, NodeJS.WritableStream, NodeJS.WritableStream, NodeJS.WritableStream, NodeJS.ReadableStream];
transport = new PipeTransport(stdio[3], stdio[4]);
}
return { browserProcess, artifactsDir: prepared.artifactsDir, userDataDir: prepared.userDataDir, transport };
return { browserProcess, artifactsDir: prepared.artifactsDir, userDataDir: prepared.userDataDir, transport, wsEndpoint };
} catch (error) {
await progress.race(closeOrKill(DEFAULT_PLAYWRIGHT_TIMEOUT).catch(() => {}));
throw error;
Expand Down
11 changes: 4 additions & 7 deletions packages/playwright-core/src/server/chromium/chromium.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export class Chromium extends BrowserType {
return super.launch(progress, options, protocolLogger);
}

override async launchPersistentContext(progress: Progress, userDataDir: string, options: channels.BrowserTypeLaunchPersistentContextOptions & { cdpPort?: number, internalIgnoreHTTPSErrors?: boolean, socksProxyPort?: number }): Promise<BrowserContext> {
override async launchPersistentContext(progress: Progress, userDataDir: string, options: channels.BrowserTypeLaunchPersistentContextOptions & { internalIgnoreHTTPSErrors?: boolean, socksProxyPort?: number }): Promise<BrowserContext> {
if (options.channel?.startsWith('bidi-'))
return this._bidiChromium.launchPersistentContext(progress, userDataDir, options);
return super.launchPersistentContext(progress, userDataDir, options);
Expand Down Expand Up @@ -343,10 +343,7 @@ export class Chromium extends BrowserType {
override async defaultArgs(options: types.LaunchOptions, isPersistent: boolean, userDataDir: string) {
const chromeArguments = this._innerDefaultArgs(options);
chromeArguments.push(`--user-data-dir=${userDataDir}`);
if (options.cdpPort !== undefined)
chromeArguments.push(`--remote-debugging-port=${options.cdpPort}`);
else
chromeArguments.push('--remote-debugging-pipe');
chromeArguments.push('--remote-debugging-pipe');
if (isPersistent)
chromeArguments.push('about:blank');
else
Expand All @@ -363,7 +360,7 @@ export class Chromium extends BrowserType {
throw new Error('Playwright manages remote debugging connection itself.');
if (args.find(arg => !arg.startsWith('-')))
throw new Error('Arguments can not specify page to be opened');
const chromeArguments = [...chromiumSwitches(options.assistantMode, options.channel)];
const chromeArguments = [...chromiumSwitches()];

// See https://issues.chromium.org/issues/40277080
chromeArguments.push('--enable-unsafe-swiftshader');
Expand Down Expand Up @@ -418,7 +415,7 @@ export class Chromium extends BrowserType {
}

export async function waitForReadyState(options: types.LaunchOptions, browserLogsCollector: RecentLogsCollector): Promise<{ wsEndpoint?: string }> {
if (options.cdpPort === undefined && !options.args?.some(a => a.startsWith('--remote-debugging-port')))
if (!options.args?.some(a => a.startsWith('--remote-debugging-port')))
return {};

const result = new ManualPromise<{ wsEndpoint?: string }>();
Expand Down
10 changes: 4 additions & 6 deletions packages/playwright-core/src/server/chromium/chromiumSwitches.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

// No dependencies as it is used from the Electron loader.

const disabledFeatures = (assistantMode?: boolean) => [
const disabledFeatures = [
// See https://github.com/microsoft/playwright/issues/14047
'AvoidUnnecessaryBeforeUnloadCheckSync',
// See https://github.com/microsoft/playwright/issues/38568
Expand All @@ -44,14 +44,13 @@ const disabledFeatures = (assistantMode?: boolean) => [
'RenderDocument',
// Prevents downloading optimization hints on startup.
'OptimizationHints',
assistantMode ? 'AutomationControlled' : '',
// Disables forced sign-in in Edge.
'msForceBrowserSignIn',
// Disables updating the preferred version in LaunchServices preferences on mac.
'msEdgeUpdateLaunchServicesPreferredVersion',
].filter(Boolean);

export const chromiumSwitches = (assistantMode?: boolean, channel?: string, android?: boolean) => [
export const chromiumSwitches = (options?: { android?: boolean }) => [
'--disable-field-trial-config', // https://source.chromium.org/chromium/chromium/src/+/main:testing/variations/README.md
'--disable-background-networking',
'--disable-background-timer-throttling',
Expand All @@ -66,7 +65,7 @@ export const chromiumSwitches = (assistantMode?: boolean, channel?: string, andr
'--disable-dev-shm-usage',
'--disable-edgeupdater', // Disables Edge-specific updater on mac.
'--disable-extensions',
'--disable-features=' + disabledFeatures(assistantMode).join(','),
'--disable-features=' + disabledFeatures.join(','),
process.env.PLAYWRIGHT_LEGACY_SCREENSHOT ? '' : '--enable-features=CDPScreenshotNewSurface',
'--allow-pre-commit-input',
'--disable-hang-monitor',
Expand All @@ -88,12 +87,11 @@ export const chromiumSwitches = (assistantMode?: boolean, channel?: string, andr
'--unsafely-disable-devtools-self-xss-warnings',
// Edge can potentially restart on Windows (msRelaunchNoCompatLayer) which looses its file descriptors (stdout/stderr) and CDP (3/4). Disable until fixed upstream.
'--edge-skip-compat-layer-relaunch',
assistantMode ? '' : '--enable-automation',
// This disables Chrome for Testing infobar that is visible in the persistent context.
// The switch is ignored everywhere else, including Chromium/Chrome/Edge.
'--disable-infobars',
// Less annoying popups.
'--disable-search-engine-choice-screen',
// Prevents the "three dots" menu crash in IdentityManager::HasPrimaryAccount for ephemeral contexts.
android ? '' : '--disable-sync',
options?.android ? '' : '--disable-sync',
].filter(Boolean);
2 changes: 1 addition & 1 deletion packages/playwright-core/src/server/firefox/firefox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export class Firefox extends BrowserType {
return super.launch(progress, options, protocolLogger);
}

override async launchPersistentContext(progress: Progress, userDataDir: string, options: channels.BrowserTypeLaunchPersistentContextOptions & { cdpPort?: number, internalIgnoreHTTPSErrors?: boolean, socksProxyPort?: number }): Promise<BrowserContext> {
override async launchPersistentContext(progress: Progress, userDataDir: string, options: channels.BrowserTypeLaunchPersistentContextOptions & { internalIgnoreHTTPSErrors?: boolean, socksProxyPort?: number }): Promise<BrowserContext> {
if (options.channel?.startsWith('moz-'))
return this._bidiFirefox.launchPersistentContext(progress, userDataDir, options);
return super.launchPersistentContext(progress, userDataDir, options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ export class RecorderApp {
persistentContextOptions: {
noDefaultViewport: true,
headless: !!process.env.PWTEST_CLI_HEADLESS || (isUnderTest() && !headed),
cdpPort: isUnderTest() ? 0 : undefined,
args: isUnderTest() ? ['--remote-debugging-port=0'] : undefined,
handleSIGINT: params.handleSIGINT,
executablePath: isChromium ? inspectedContext._browser.options.customExecutablePath : undefined,
// Use the same channel as the inspected context to guarantee that the browser is installed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ export async function openTraceViewerApp(url: string, browserName: string, optio
windowSize: { width: 1280, height: 800 },
persistentContextOptions: {
...options?.persistentContextOptions,
cdpPort: isUnderTest() ? 0 : undefined,
args: isUnderTest() ? ['--remote-debugging-port=0'] : undefined,
headless: !!options?.headless,
colorScheme: isUnderTest() ? 'light' : undefined,
},
Expand Down
2 changes: 0 additions & 2 deletions packages/playwright-core/src/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,7 @@ export type NormalizedContinueOverrides = {
export type EmulatedSize = { viewport: Size, screen: Size };

export type LaunchOptions = Omit<channels.BrowserTypeLaunchParams, 'timeout'> & {
cdpPort?: number,
proxyOverride?: ProxySettings,
assistantMode?: boolean,
socksProxyPort?: number,
};

Expand Down
2 changes: 1 addition & 1 deletion packages/playwright-core/src/serverRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class ServerRegistry {
const entries = await Promise.all(promises);
const descriptors = [];
for (const entry of entries) {
if (!entry.canConnect && !entry.browser.userDataDir) {
if (!entry.canConnect) {
await fs.promises.unlink(entry.file).catch(() => {});
continue;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/playwright-core/src/tools/cli-client/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export class Session {
}

async deleteData() {
await this.stop();
await this.stop(true);

const dataDirs = await fs.promises.readdir(this._sessionFile.daemonDir).catch(() => []);
const matchingEntries = dataDirs.filter(file => file === `${this.name}.session` || file.startsWith(`ud-${this.name}-`));
Expand Down
7 changes: 4 additions & 3 deletions packages/playwright-core/src/tools/mcp/browserFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,10 @@ async function createUserDataDir(config: FullConfig, clientInfo: ClientInfo) {
}

async function injectCdpPort(browserConfig: FullConfig['browser']) {
if (browserConfig.browserName === 'chromium')
// eslint-disable-next-line no-restricted-syntax
(browserConfig.launchOptions as any).cdpPort = await findFreePort();
if (browserConfig.browserName === 'chromium') {
browserConfig.launchOptions.args = browserConfig.launchOptions.args ?? [];
browserConfig.launchOptions.args?.push(`--remote-debugging-port=${await findFreePort()}`);
}
}

async function findFreePort(): Promise<number> {
Expand Down
6 changes: 3 additions & 3 deletions packages/playwright-core/src/tools/mcp/cdpRelay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
* - /extension/guid - Extension connection
*
* Protocol version is controlled by PLAYWRIGHT_EXTENSION_PROTOCOL env variable:
* - v1: single-tab, extension manages debugger attachment
* - v2 (default): multi-tab, relay manages debugger via chrome.* APIs
* - v1 (default): single-tab, extension manages debugger attachment
* - v2: multi-tab, relay manages debugger via chrome.* APIs
*/

import { spawn } from 'child_process';
Expand Down Expand Up @@ -77,7 +77,7 @@ export class CDPRelayServer {
this._browserChannel = browserChannel;
this._userDataDir = userDataDir;
this._executablePath = executablePath;
this._protocolVersion = parseInt(process.env.PLAYWRIGHT_EXTENSION_PROTOCOL ?? protocol.VERSION.toString(), 10);
this._protocolVersion = parseInt(process.env.PLAYWRIGHT_EXTENSION_PROTOCOL ?? protocol.DEFAULT_VERSION.toString(), 10);

const sendCommand = (method: string, params: any): Promise<any> => {
if (!this._extensionConnection)
Expand Down
17 changes: 8 additions & 9 deletions packages/playwright-core/src/tools/mcp/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,17 +163,18 @@ export async function resolveCLIConfigForCLI(daemonProfilesDir: string, sessionN
if (result.browser.isolated === undefined)
result.browser.isolated = !options.profile && !options.persistent && !result.browser.userDataDir && !result.browser.remoteEndpoint && !result.browser.cdpEndpoint && !result.extension;

if (!result.extension && !result.browser.isolated && !result.browser.userDataDir && !result.browser.remoteEndpoint && !result.browser.cdpEndpoint) {
// No custom value provided, use the daemon data dir.
const browserToken = result.browser.launchOptions?.channel ?? result.browser?.browserName;
const userDataDir = path.resolve(daemonProfilesDir, `ud-${sessionName}-${browserToken}`);
result.browser.userDataDir = userDataDir;
}

if (result.browser.launchOptions.headless === undefined)
result.browser.launchOptions.headless = true;

const browser = await validateBrowserConfig(result.browser);

if (!result.extension && !browser.isolated && !browser.userDataDir && !browser.remoteEndpoint && !browser.cdpEndpoint) {
// No custom value provided, use the daemon data dir.
const browserToken = browser.launchOptions?.channel ?? browser?.browserName;
const userDataDir = path.resolve(daemonProfilesDir, `ud-${sessionName}-${browserToken}`);
browser.userDataDir = userDataDir;
}

return { ...result, browser, configFile, skillMode: true };
}

Expand Down Expand Up @@ -412,8 +413,6 @@ function mergeConfig(base: MergedConfig, overrides: Config): MergedConfig {
launchOptions: {
...pickDefined(base.browser?.launchOptions),
...pickDefined(overrides.browser?.launchOptions),
// Assistant mode is not a part of the public API.
...{ assistantMode: true },
},
contextOptions: {
...pickDefined(base.browser?.contextOptions),
Expand Down
11 changes: 8 additions & 3 deletions packages/playwright-core/src/tools/mcp/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@
* limitations under the License.
*/

// Whenever the commands/events change, the version must be updated. The latest
// extension version should be compatible with the old MCP clients.
export const VERSION = 2;
// The latest protocol version defined in this file. Bumped whenever the
// commands/events change. The latest extension version should remain
// compatible with older MCP clients.
export const LATEST_VERSION = 2;

// The protocol version used by default when PLAYWRIGHT_EXTENSION_PROTOCOL is
// not set. May lag behind LATEST_VERSION while a new version is rolling out.
export const DEFAULT_VERSION = 1;

// Structural mirrors of @types/chrome shapes used over the wire. The extension
// imports the real chrome.* types and they are structurally compatible.
Expand Down
4 changes: 0 additions & 4 deletions packages/protocol/src/channels.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -902,7 +902,6 @@ export type BrowserTypeLaunchParams = {
args?: string[],
ignoreAllDefaultArgs?: boolean,
ignoreDefaultArgs?: string[],
assistantMode?: boolean,
handleSIGINT?: boolean,
handleSIGTERM?: boolean,
handleSIGHUP?: boolean,
Expand All @@ -929,7 +928,6 @@ export type BrowserTypeLaunchOptions = {
args?: string[],
ignoreAllDefaultArgs?: boolean,
ignoreDefaultArgs?: string[],
assistantMode?: boolean,
handleSIGINT?: boolean,
handleSIGTERM?: boolean,
handleSIGHUP?: boolean,
Expand Down Expand Up @@ -958,7 +956,6 @@ export type BrowserTypeLaunchPersistentContextParams = {
args?: string[],
ignoreAllDefaultArgs?: boolean,
ignoreDefaultArgs?: string[],
assistantMode?: boolean,
handleSIGINT?: boolean,
handleSIGTERM?: boolean,
handleSIGHUP?: boolean,
Expand Down Expand Up @@ -1047,7 +1044,6 @@ export type BrowserTypeLaunchPersistentContextOptions = {
args?: string[],
ignoreAllDefaultArgs?: boolean,
ignoreDefaultArgs?: string[],
assistantMode?: boolean,
handleSIGINT?: boolean,
handleSIGTERM?: boolean,
handleSIGHUP?: boolean,
Expand Down
1 change: 0 additions & 1 deletion packages/protocol/src/protocol.yml
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,6 @@ LaunchOptions:
ignoreDefaultArgs:
type: array?
items: string
assistantMode: boolean?
handleSIGINT: boolean?
handleSIGTERM: boolean?
handleSIGHUP: boolean?
Expand Down
Loading
Loading