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: 1 addition & 1 deletion packages/playwright-core/src/tools/mcp/browserFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export async function createBrowserWithInfo(config: FullConfig, clientInfo: Clie
ownership = 'own';
} else if (config.extension) {
const { channel, executablePath } = resolveExtensionOptions(cliOptions);
browser = await createExtensionBrowser(channel, executablePath, clientInfo.clientName);
browser = await createExtensionBrowser(channel, executablePath, config.browser.userDataDir, clientInfo.clientName);
ownership = 'attached';
} else {
browser = await createPersistentBrowser(config, clientInfo);
Expand Down
22 changes: 11 additions & 11 deletions packages/playwright-core/src/tools/mcp/cdpRelay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { ManualPromise } from '@isomorphic/manualPromise';
import { WSServer } from '@utils/wsServer';
import { registry } from '../../server/registry/index';

import { findPlaywrightExtensionProfile, playwrightExtensionId } from '../utils/extension';
import { playwrightExtensionId } from '../utils/extension';
import { logUnhandledError } from './log';
import { ExtensionProtocolV2 } from './cdpRelayV2';
import * as protocol from './protocol';
Expand All @@ -61,7 +61,8 @@ export class CDPRelayServer {
private _wsHost!: string;
private _browserChannel: string;
private _executablePath?: string;
private _userDataDir?: string;
private _customUserDataDir?: string;
private _profileDirectory?: string;
private _cdpPath: string;
private _extensionPath: string;
private _cdpConnection: WebSocket | null = null;
Expand All @@ -70,10 +71,11 @@ export class CDPRelayServer {
private _handler: ExtensionProtocolV2;
private _extensionConnectionPromise = new ManualPromise<void>();

constructor(browserChannel: string, executablePath?: string, userDataDir?: string) {
constructor(browserChannel: string, executablePath?: string, customUserDataDir?: string, profileDirectory?: string) {
this._browserChannel = browserChannel;
this._executablePath = executablePath;
this._userDataDir = userDataDir;
this._customUserDataDir = customUserDataDir;
this._profileDirectory = profileDirectory;
this._protocolVersion = parseInt(process.env.PLAYWRIGHT_EXTENSION_PROTOCOL ?? protocol.VERSION.toString(), 10);

const sendCommand = (method: string, params: any): Promise<any> => {
Expand Down Expand Up @@ -156,13 +158,11 @@ export class CDPRelayServer {
}

const args: string[] = [];
const testUserDataDir = process.env.PWTEST_EXTENSION_USER_DATA_DIR;
if (testUserDataDir)
args.push(`--user-data-dir=${testUserDataDir}`);
const userDataDir = testUserDataDir ?? this._userDataDir;
const profileDirectory = userDataDir ? await findPlaywrightExtensionProfile(userDataDir) : undefined;
if (profileDirectory)
args.push(`--profile-directory=${profileDirectory}`);
// The default profile dir is not passed explicitly, the browser resolves it on its own.
if (this._customUserDataDir)
args.push(`--user-data-dir=${this._customUserDataDir}`);
if (this._profileDirectory)
args.push(`--profile-directory=${this._profileDirectory}`);
if (os.platform() === 'linux' && channel === 'chromium')
args.push('--no-sandbox');
args.push(href);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,22 @@
import debug from 'debug';
import { defaultUserDataDirForChannel } from '@utils/chromiumChannels';
import { playwright } from '../../inprocess';
import { isPlaywrightExtensionInstalled, playwrightExtensionInstallUrl } from '../utils/extension';
import { findPlaywrightExtensionProfile, playwrightExtensionInstallUrl } from '../utils/extension';
import { CDPRelayServer } from './cdpRelay';

import type * as playwrightTypes from '../../..';

const debugLogger = debug('pw:mcp:relay');

export async function createExtensionBrowser(channel: string, executablePath: string | undefined, clientName: string): Promise<playwrightTypes.Browser> {
export async function createExtensionBrowser(channel: string, executablePath: string | undefined, customUserDataDir: string | undefined, clientName: string): Promise<playwrightTypes.Browser> {
customUserDataDir ??= process.env.PWTEST_EXTENSION_USER_DATA_DIR;
// Custom executablePath may target a browser in a different filesystem (e.g. Windows chrome.exe from WSL2), so the local profile path is not meaningful.
let userDataDir: string | undefined;
if (!executablePath) {
userDataDir = process.env.PWTEST_EXTENSION_USER_DATA_DIR ?? defaultUserDataDirForChannel(channel);
if (userDataDir && !await isPlaywrightExtensionInstalled(userDataDir))
throw new Error(`Playwright Extension not found in "${userDataDir}". Install it from ${playwrightExtensionInstallUrl}, or set the PLAYWRIGHT_MCP_EXECUTABLE_PATH environment variable to use a browser at a custom location.`);
}
const userDataDir = customUserDataDir ?? (executablePath ? undefined : defaultUserDataDirForChannel(channel));
const profileDirectory = userDataDir ? await findPlaywrightExtensionProfile(userDataDir) : undefined;
if (!executablePath && userDataDir && !profileDirectory)
throw new Error(`Playwright Extension not found in "${userDataDir}". Install it from ${playwrightExtensionInstallUrl}, or set the PLAYWRIGHT_MCP_EXECUTABLE_PATH environment variable to use a browser at a custom location.`);

const relay = new CDPRelayServer(channel, executablePath, userDataDir);
const relay = new CDPRelayServer(channel, executablePath, customUserDataDir, profileDirectory);
await relay.start();
debugLogger(`CDP relay server started, extension endpoint: ${relay.extensionEndpoint()}.`);

Expand Down
15 changes: 15 additions & 0 deletions tests/extension/extension.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,21 @@ test(`fails when extension is missing in custom userDataDir`, async ({ startClie
});
});

test(`navigate with extension via --user-data-dir`, {
annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/42163' },
}, async ({ browserWithExtension, startClient, server }) => {
const browserContext = await browserWithExtension.launch();

const { client } = await startClient({
args: [`--extension`, `--user-data-dir=${browserWithExtension.userDataDir}`],
});

const response = await connectAndNavigate(browserContext, client, server.HELLO_WORLD);
expect(response).toHaveResponse({
snapshot: expect.stringContaining(`Hello, world!`),
});
});

test(`--browser <channel> selects channel-specific userDataDir`, {
annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright-mcp/issues/1589' },
}, async ({ startClient, server }) => {
Expand Down
Loading