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
16 changes: 7 additions & 9 deletions docs/src/api/class-inspector.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ console.log(locator);
* since: v1.59
- argument: <[Object]>
- `data` <[Buffer]> JPEG-encoded frame data.
- `width` <[int]> Frame width in pixels.
- `height` <[int]> Frame height in pixels.

Emitted for each captured JPEG screencast frame while the screencast is running.

Expand All @@ -41,7 +39,7 @@ inspector.on('screencastframe', ({ data, width, height }) => {
console.log(`frame ${width}x${height}, jpeg size: ${data.length}`);
require('fs').writeFileSync('frame.jpg', data);
});
await inspector.startScreencast({ size: { width: 1280, height: 720 } });
await inspector.startScreencast({ maxSize: { width: 1200, height: 800 } });
// ... perform actions ...
await inspector.stopScreencast();
```
Expand All @@ -58,18 +56,18 @@ const inspector = page.inspector();
inspector.on('screencastframe', ({ data, width, height }) => {
console.log(`frame ${width}x${height}, size: ${data.length}`);
});
await inspector.startScreencast({ size: { width: 800, height: 600 } });
await inspector.startScreencast({ maxSize: { width: 800, height: 600 } });
// ... perform actions ...
await inspector.stopScreencast();
```

### option: Inspector.startScreencast.size
### option: Inspector.startScreencast.maxSize
* since: v1.59
- `size` ?<[Object]>
- `width` <[int]> Frame width in pixels.
- `height` <[int]> Frame height in pixels.
- `maxSize` ?<[Object]>
- `width` <[int]> Max frame width in pixels.
- `height` <[int]> Max frame height in pixels.

Optional dimensions for the screencast frames. If not specified, the current page viewport size is used.
Maximum screencast frame dimensions. The output frame may be smaller to preserve the page aspect ratio. Defaults to 800×800.

## async method: Inspector.stopScreencast
* since: v1.59
Expand Down
77 changes: 9 additions & 68 deletions packages/playwright-client/types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20445,7 +20445,7 @@ export interface Inspector {
* console.log(`frame ${width}x${height}, jpeg size: ${data.length}`);
* require('fs').writeFileSync('frame.jpg', data);
* });
* await inspector.startScreencast({ size: { width: 1280, height: 720 } });
* await inspector.startScreencast({ maxSize: { width: 1200, height: 800 } });
* // ... perform actions ...
* await inspector.stopScreencast();
* ```
Expand All @@ -20456,16 +20456,6 @@ export interface Inspector {
* JPEG-encoded frame data.
*/
data: Buffer;

/**
* Frame width in pixels.
*/
width: number;

/**
* Frame height in pixels.
*/
height: number;
}) => any): this;

/**
Expand All @@ -20476,16 +20466,6 @@ export interface Inspector {
* JPEG-encoded frame data.
*/
data: Buffer;

/**
* Frame width in pixels.
*/
width: number;

/**
* Frame height in pixels.
*/
height: number;
}) => any): this;

/**
Expand All @@ -20499,7 +20479,7 @@ export interface Inspector {
* console.log(`frame ${width}x${height}, jpeg size: ${data.length}`);
* require('fs').writeFileSync('frame.jpg', data);
* });
* await inspector.startScreencast({ size: { width: 1280, height: 720 } });
* await inspector.startScreencast({ maxSize: { width: 1200, height: 800 } });
* // ... perform actions ...
* await inspector.stopScreencast();
* ```
Expand All @@ -20510,16 +20490,6 @@ export interface Inspector {
* JPEG-encoded frame data.
*/
data: Buffer;

/**
* Frame width in pixels.
*/
width: number;

/**
* Frame height in pixels.
*/
height: number;
}) => any): this;

/**
Expand All @@ -20530,16 +20500,6 @@ export interface Inspector {
* JPEG-encoded frame data.
*/
data: Buffer;

/**
* Frame width in pixels.
*/
width: number;

/**
* Frame height in pixels.
*/
height: number;
}) => any): this;

/**
Expand All @@ -20550,16 +20510,6 @@ export interface Inspector {
* JPEG-encoded frame data.
*/
data: Buffer;

/**
* Frame width in pixels.
*/
width: number;

/**
* Frame height in pixels.
*/
height: number;
}) => any): this;

/**
Expand All @@ -20573,7 +20523,7 @@ export interface Inspector {
* console.log(`frame ${width}x${height}, jpeg size: ${data.length}`);
* require('fs').writeFileSync('frame.jpg', data);
* });
* await inspector.startScreencast({ size: { width: 1280, height: 720 } });
* await inspector.startScreencast({ maxSize: { width: 1200, height: 800 } });
* // ... perform actions ...
* await inspector.stopScreencast();
* ```
Expand All @@ -20584,16 +20534,6 @@ export interface Inspector {
* JPEG-encoded frame data.
*/
data: Buffer;

/**
* Frame width in pixels.
*/
width: number;

/**
* Frame height in pixels.
*/
height: number;
}) => any): this;

/**
Expand Down Expand Up @@ -20630,7 +20570,7 @@ export interface Inspector {
* inspector.on('screencastframe', ({ data, width, height }) => {
* console.log(`frame ${width}x${height}, size: ${data.length}`);
* });
* await inspector.startScreencast({ size: { width: 800, height: 600 } });
* await inspector.startScreencast({ maxSize: { width: 800, height: 600 } });
* // ... perform actions ...
* await inspector.stopScreencast();
* ```
Expand All @@ -20639,16 +20579,17 @@ export interface Inspector {
*/
startScreencast(options?: {
/**
* Optional dimensions for the screencast frames. If not specified, the current page viewport size is used.
* Maximum screencast frame dimensions. The output frame may be smaller to preserve the page aspect ratio. Defaults to
* 800×800.
*/
size?: {
maxSize?: {
/**
* Frame width in pixels.
* Max frame width in pixels.
*/
width: number;

/**
* Frame height in pixels.
* Max frame height in pixels.
*/
height: number;
};
Expand Down
8 changes: 5 additions & 3 deletions packages/playwright-core/src/cli/client/devtoolsApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ import path from 'path';
import os from 'os';
import net from 'net';


import { chromium } from '../../..';
import { HttpServer } from '../../server/utils/httpServer';
import { gracefullyProcessExitDoNotHang } from '../../server/utils/processLauncher';
import { findChromiumChannelBestEffort, registryDirectory } from '../../server/registry/index';

import { calculateSha1 } from '../../utils';
import { createClientInfo, Registry } from './registry';
import { Session } from './session';

Expand Down Expand Up @@ -213,9 +214,10 @@ function socketsDirectory() {
}

function devtoolsSocketPath() {
const userNameHash = calculateSha1(process.env.USERNAME || 'default').slice(0, 8);
return process.platform === 'win32'
? `\\\\.\\pipe\\playwright-devtools-${process.env.USERNAME || 'default'}`
: path.join(socketsDirectory(), 'devtools.sock');
? `\\\\.\\pipe\\playwright-devtools-${userNameHash}`
: path.join(socketsDirectory(), `devtools-${userNameHash}.sock`);
}

async function acquireSingleton(): Promise<net.Server> {
Expand Down
10 changes: 9 additions & 1 deletion packages/playwright-core/src/cli/client/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import { execSync, spawn } from 'child_process';

import crypto from 'crypto';
import fs from 'fs';
import os from 'os';
import path from 'path';
Expand Down Expand Up @@ -298,7 +299,8 @@ async function findOrInstallDefaultBrowser() {
}

function daemonSocketPath(clientInfo: ClientInfo, sessionName: string): string {
const socketName = `${sessionName}.sock`;
const userNameHash = calculateSha1(process.env.USERNAME || 'default').slice(0, 8);
const socketName = `${sessionName}-${userNameHash}.sock`;
if (os.platform() === 'win32')
return `\\\\.\\pipe\\${clientInfo.workspaceDirHash}-${socketName}`;
const socketsDir = process.env.PLAYWRIGHT_DAEMON_SOCKETS_DIR || path.join(os.tmpdir(), 'playwright-cli');
Expand Down Expand Up @@ -442,3 +444,9 @@ async function renderSessionStatus(clientInfo: ClientInfo, session: Session) {
text.push(...renderResolvedConfig(config.resolvedConfig));
return text.join('\n');
}

export function calculateSha1(buffer: Buffer | string): string {
const hash = crypto.createHash('sha1');
hash.update(buffer);
return hash.digest('hex');
}
2 changes: 1 addition & 1 deletion packages/playwright-core/src/cli/daemon/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -875,7 +875,7 @@ const installBrowser = declareCommand({
['only-shell']: z.boolean().optional().describe('Only install headless shell when installing Chromium'),
['no-shell']: z.boolean().optional().describe('Do not install Chromium headless shell'),
}),
toolName: 'browser_install',
toolName: '',
toolParams: () => ({}),
});

Expand Down
31 changes: 13 additions & 18 deletions packages/playwright-core/src/cli/daemon/daemon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,8 @@ import { SocketConnection } from '../client/socketConnection';
import { commands } from './commands';
import { parseCommand } from './command';

import type * as playwright from '../../..';
import type * as mcp from '../../mcp/exports';
import type { FullConfig } from '../../mcp/browser/config';
import type { BrowserContextFactory } from '../../mcp/browser/browserContextFactory';
import type { SessionConfig } from '../client/registry';

const daemonDebug = debug('pw:daemon');
Expand All @@ -48,9 +47,9 @@ async function socketExists(socketPath: string): Promise<boolean> {
}

export async function startMcpDaemonServer(
mcpConfig: FullConfig,
config: mcp.ContextConfig,
sessionConfig: SessionConfig,
contextFactory: BrowserContextFactory,
browserContext: playwright.BrowserContext,
noShutdown?: boolean,
): Promise<() => Promise<void>> {
const { socketPath } = sessionConfig;
Expand All @@ -65,27 +64,23 @@ export async function startMcpDaemonServer(
}
}

const cwd = url.pathToFileURL(process.cwd()).href;
const clientInfo = {
name: 'playwright-cli',
version: sessionConfig.version,
roots: [{
uri: cwd,
name: 'cwd'
}],
timestamp: Date.now(),
};

const browserContext = mcpConfig.browser.isolated ? await contextFactory.createContext(clientInfo) : (await contextFactory.contexts(clientInfo))[0];
if (!noShutdown) {
browserContext.on('close', () => {
daemonDebug('browser closed, shutting down daemon');
shutdown(0);
});
}

const backend = new BrowserServerBackend(mcpConfig, browserContext, browserTools);
await backend.initialize(clientInfo);
const backend = new BrowserServerBackend(config, browserContext, browserTools);
await backend.initialize({
name: 'playwright-cli',
version: sessionConfig.version,
roots: [{
uri: url.pathToFileURL(process.cwd()).href,
name: 'cwd',
}],
timestamp: Date.now(),
});

await fs.mkdir(path.dirname(socketPath), { recursive: true });

Expand Down
20 changes: 17 additions & 3 deletions packages/playwright-core/src/cli/daemon/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
/* eslint-disable no-console */

import fs from 'fs';
import url from 'url';

import { startMcpDaemonServer } from './daemon';
import { setupExitWatchdog } from '../../mcp/browser/watchdog';
Expand All @@ -38,13 +39,26 @@ export function decorateCLICommand(command: Command, version: string) {
setupExitWatchdog();

const sessionConfig = await fs.promises.readFile(options.daemonSession, 'utf-8').then(data => JSON.parse(data) as SessionConfig);

const cwd = url.pathToFileURL(process.cwd()).href;
const clientInfo = {
name: 'playwright-cli',
version: sessionConfig.version,
roots: [{
uri: cwd,
name: 'cwd'
}],
timestamp: Date.now(),
};

const mcpConfig = await resolveCLIConfig(sessionConfig);
const browserContextFactory = contextFactory(mcpConfig);
const extensionContextFactory = new ExtensionContextFactory(mcpConfig.browser.launchOptions.channel || 'chrome', mcpConfig.browser.userDataDir, mcpConfig.browser.launchOptions.executablePath);

const browserContextFactory = contextFactory(mcpConfig);
const cf = mcpConfig.extension ? extensionContextFactory : browserContextFactory;

try {
await startMcpDaemonServer(mcpConfig, sessionConfig, cf);
const browserContext = mcpConfig.browser.isolated ? await cf.createContext(clientInfo) : (await cf.contexts(clientInfo))[0];
await startMcpDaemonServer(mcpConfig, sessionConfig, browserContext);
console.log(`### Config`);
console.log('```json');
console.log(JSON.stringify(mcpConfig, null, 2));
Expand Down
6 changes: 4 additions & 2 deletions packages/playwright-core/src/client/disposable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ export class DisposableStub implements Disposable {
}
}

export function disposeAll(disposables: Disposable[]) {
return Promise.all(disposables.map(d => d.dispose()));
export async function disposeAll(disposables: Disposable[]) {
const copy = [...disposables];
disposables.length = 0;
await Promise.all(copy.map(d => d.dispose()));
}
4 changes: 2 additions & 2 deletions packages/playwright-core/src/client/inspector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class Inspector extends EventEmitter implements api.Inspector {
constructor(page: Page) {
super(page._platform);
this._page = page;
this._page._channel.on('screencastFrame', ({ data, width, height }) => this.emit('screencastframe', { data, width, height }));
this._page._channel.on('screencastFrame', ({ data }) => this.emit('screencastframe', { data }));
}

async pickLocator(): Promise<Locator> {
Expand All @@ -38,7 +38,7 @@ export class Inspector extends EventEmitter implements api.Inspector {
await this._page._channel.cancelPickLocator({});
}

async startScreencast(options: { size?: { width: number, height: number } } = {}): Promise<void> {
async startScreencast(options: { maxSize?: { width: number, height: number } } = {}): Promise<void> {
await this._page._channel.startScreencast(options);
}

Expand Down
Loading
Loading