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
36 changes: 36 additions & 0 deletions docs/src/release-notes-csharp.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,42 @@ title: "Release notes"
toc_max_heading_level: 2
---

## Version 1.58

### UI Mode and Trace Viewer Improvements

- New 'system' theme option follows your OS dark/light mode preference
- Search functionality (Cmd/Ctrl+F) is now available in code editors
- Network details panel has been reorganized for better usability
- JSON responses are now automatically formatted for readability

Thanks to [@cpAdm](https://github.com/cpAdm) for contributing these improvements!

### Miscellaneous

[`method: BrowserType.connectOverCDP`] now accepts an `IsLocal` option. When set to `true`, it tells Playwright that it runs on the same host as the CDP server, enabling file system optimizations.

### Breaking Changes ⚠️

- Removed `_react` and `_vue` selectors. See [locators guide](./locators.md) for alternatives.

- Removed `:light` selector engine suffix. Use standard CSS selectors instead.

- Option `Devtools` from [`method: BrowserType.launch`] has been removed. Use `Args = new[] { "--auto-open-devtools-for-tabs" }` instead.

- Removed macOS 13 support for WebKit. We recommend to upgrade your macOS version, or keep using an older Playwright version.

### Browser Versions

- Chromium 145.0.7632.6
- Mozilla Firefox 146.0.1
- WebKit 26.0

This version was also tested against the following stable channels:

- Google Chrome 144
- Microsoft Edge 144

## Version 1.57

### Chrome for Testing
Expand Down
36 changes: 36 additions & 0 deletions docs/src/release-notes-java.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,42 @@ title: "Release notes"
toc_max_heading_level: 2
---

## Version 1.58

### UI Mode and Trace Viewer Improvements

- New 'system' theme option follows your OS dark/light mode preference
- Search functionality (Cmd/Ctrl+F) is now available in code editors
- Network details panel has been reorganized for better usability
- JSON responses are now automatically formatted for readability

Thanks to [@cpAdm](https://github.com/cpAdm) for contributing these improvements!

### Miscellaneous

[`method: BrowserType.connectOverCDP`] now accepts an `isLocal` option. When set to `true`, it tells Playwright that it runs on the same host as the CDP server, enabling file system optimizations.

### Breaking Changes ⚠️

- Removed `_react` and `_vue` selectors. See [locators guide](./locators.md) for alternatives.

- Removed `:light` selector engine suffix. Use standard CSS selectors instead.

- Option `devtools` from [`method: BrowserType.launch`] has been removed. Use `setArgs(Arrays.asList("--auto-open-devtools-for-tabs"))` instead.

- Removed macOS 13 support for WebKit. We recommend to upgrade your macOS version, or keep using an older Playwright version.

### Browser Versions

- Chromium 145.0.7632.6
- Mozilla Firefox 146.0.1
- WebKit 26.0

This version was also tested against the following stable channels:

- Google Chrome 144
- Microsoft Edge 144

## Version 1.57

### Chrome for Testing
Expand Down
36 changes: 36 additions & 0 deletions docs/src/release-notes-python.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,42 @@ title: "Release notes"
toc_max_heading_level: 2
---

## Version 1.58

### UI Mode and Trace Viewer Improvements

- New 'system' theme option follows your OS dark/light mode preference
- Search functionality (Cmd/Ctrl+F) is now available in code editors
- Network details panel has been reorganized for better usability
- JSON responses are now automatically formatted for readability

Thanks to [@cpAdm](https://github.com/cpAdm) for contributing these improvements!

### Miscellaneous

[`method: BrowserType.connectOverCDP`] now accepts an `is_local` option. When set to `True`, it tells Playwright that it runs on the same host as the CDP server, enabling file system optimizations.

### Breaking Changes ⚠️

- Removed `_react` and `_vue` selectors. See [locators guide](./locators.md) for alternatives.

- Removed `:light` selector engine suffix. Use standard CSS selectors instead.

- Option `devtools` from [`method: BrowserType.launch`] has been removed. Use `args=['--auto-open-devtools-for-tabs']` instead.

- Removed macOS 13 support for WebKit. We recommend to upgrade your macOS version, or keep using an older Playwright version.

### Browser Versions

- Chromium 145.0.7632.6
- Mozilla Firefox 146.0.1
- WebKit 26.0

This version was also tested against the following stable channels:

- Google Chrome 144
- Microsoft Edge 144

## Version 1.57

### Chrome for Testing
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 17 additions & 6 deletions packages/playwright-core/src/server/bidi/bidiNetworkManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,16 @@ export class BidiNetworkManager {
if (redirectedFrom)
this._deleteRequest(redirectedFrom._id);
let route;
let headersOverride: types.HeadersArray | undefined;
if (param.intercepts) {
// We do not support intercepting redirects.
if (redirectedFrom) {
let params = {};
if (redirectedFrom._originalRequestRoute?._alreadyContinuedHeaders)
params = toBidiRequestHeaders(redirectedFrom._originalRequestRoute._alreadyContinuedHeaders ?? []);
if (redirectedFrom._originalRequestRoute?._alreadyContinuedHeaders) {
const originalHeaders = fromBidiHeaders(param.request.headers);
headersOverride = network.applyHeadersOverrides(originalHeaders, redirectedFrom._originalRequestRoute._alreadyContinuedHeaders);
params = toBidiRequestHeaders(headersOverride);
}

this._session.sendMayFail('network.continueRequest', {
request: param.request.request,
Expand All @@ -79,7 +83,7 @@ export class BidiNetworkManager {
route = new BidiRouteImpl(this._session, param.request.request);
}
}
const request = new BidiRequest(frame, redirectedFrom, param, route);
const request = new BidiRequest(frame, redirectedFrom, param, route, headersOverride);
this._requests.set(request._id, request);
this._page.frameManager.requestStarted(request.request, route);
}
Expand Down Expand Up @@ -236,14 +240,21 @@ class BidiRequest {
// store the first and only Route in the chain (if any).
_originalRequestRoute: BidiRouteImpl | undefined;

constructor(frame: frames.Frame, redirectedFrom: BidiRequest | null, payload: bidi.Network.BeforeRequestSentParameters, route: BidiRouteImpl | undefined) {
constructor(
frame: frames.Frame,
redirectedFrom: BidiRequest | null,
payload: bidi.Network.BeforeRequestSentParameters,
route: BidiRouteImpl | undefined,
headersOverride: types.HeadersArray | undefined
) {
this._id = payload.request.request;
if (redirectedFrom)
redirectedFrom._redirectedTo = this;
// TODO: missing in the spec?
const postDataBuffer = null;
this.request = new network.Request(frame._page.browserContext, frame, null, redirectedFrom ? redirectedFrom.request : null, payload.navigation ?? undefined, payload.request.url,
resourceTypeFromBidi(payload.request.destination, payload.request.initiatorType, payload.initiator?.type), payload.request.method, postDataBuffer, fromBidiHeaders(payload.request.headers));
this.request = new network.Request(frame._page.browserContext, frame, null, redirectedFrom ? redirectedFrom.request : null, payload.navigation ?? undefined,
payload.request.url, resourceTypeFromBidi(payload.request.destination, payload.request.initiatorType, payload.initiator?.type), payload.request.method,
postDataBuffer, headersOverride || fromBidiHeaders(payload.request.headers));
// "raw" headers are the same as "provisional" headers in Bidi.
this.request.setRawRequestHeaders(null);
this.request._setBodySize(payload.request.bodySize || 0);
Expand Down
4 changes: 2 additions & 2 deletions packages/playwright-core/src/server/electron/electron.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,8 @@ export class Electron extends SdkObject {

async launch(progress: Progress, options: Omit<channels.ElectronLaunchParams, 'timeout'>): Promise<ElectronApplication> {
let app: ElectronApplication | undefined = undefined;
// --remote-debugging-port=0 must be the last playwright's argument, loader.ts relies on it.
let electronArguments = ['--inspect=0', '--remote-debugging-port=0', ...(options.args || [])];
// --inspect=0 must be the last playwright's argument, loader.ts relies on it.
let electronArguments = ['--inspect=0', ...(options.args || [])];

if (os.platform() === 'linux') {
if (!options.chromiumSandbox && electronArguments.indexOf('--no-sandbox') === -1)
Expand Down
4 changes: 3 additions & 1 deletion packages/playwright-core/src/server/electron/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ const { chromiumSwitches } = require('../chromium/chromiumSwitches');
// Always pass user arguments first, see https://github.com/microsoft/playwright/issues/16614 and
// https://github.com/microsoft/playwright/issues/29198.
// [Electron, -r, loader.js[, --no-sandbox>], --inspect=0, --remote-debugging-port=0, ...args]
process.argv.splice(1, process.argv.indexOf('--remote-debugging-port=0'));
process.argv.splice(1, process.argv.indexOf('--inspect=0'));

app.commandLine.appendSwitch('remote-debugging-port', '0');

for (const arg of chromiumSwitches()) {
const match = arg.match(/--([^=]*)=?(.*)/)!;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ export class SharedContextFactory implements BrowserContextFactory {
}

async function computeTracesDir(config: FullConfig, clientInfo: ClientInfo): Promise<string | undefined> {
if (!config.saveTrace && !config.capabilities?.includes('tracing'))
if (!config.saveTrace && !config.capabilities?.includes('devtools'))
return;
return await outputFile(config, clientInfo, `traces`, { origin: 'code', title: 'Collecting trace' });
}
Expand Down
6 changes: 4 additions & 2 deletions packages/playwright/src/mcp/browser/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ import screenshot from './tools/screenshot';
import storage from './tools/storage';
import tabs from './tools/tabs';
import tracing from './tools/tracing';
import wait from './tools/wait';
import verify from './tools/verify';
import video from './tools/video';
import wait from './tools/wait';

import type { Tool } from './tools/tool';
import type { FullConfig } from './config';
Expand All @@ -57,8 +58,9 @@ export const browserTools: Tool<any>[] = [
...storage,
...tabs,
...tracing,
...wait,
...verify,
...video,
...wait,
];

export function filteredTools(config: FullConfig) {
Expand Down
4 changes: 2 additions & 2 deletions packages/playwright/src/mcp/browser/tools/tracing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { defineTool } from './tool';
import type { Tracing } from '../../../../../playwright-core/src/client/tracing';

const tracingStart = defineTool({
capability: 'tracing',
capability: 'devtools',

schema: {
name: 'browser_start_tracing',
Expand Down Expand Up @@ -50,7 +50,7 @@ const tracingStart = defineTool({
});

const tracingStop = defineTool({
capability: 'tracing',
capability: 'devtools',

schema: {
name: 'browser_stop_tracing',
Expand Down
71 changes: 71 additions & 0 deletions packages/playwright/src/mcp/browser/tools/video.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { z } from 'playwright-core/lib/mcpBundle';
import { defineTabTool } from './tool';
import { dateAsFileName } from './utils';

const startVideo = defineTabTool({
capability: 'devtools',

schema: {
name: 'browser_start_video',
title: 'Start video',
description: 'Start video recording',
inputSchema: z.object({
size: z.object({
width: z.number().describe('Video width'),
height: z.number().describe('Video height'),
}).optional().describe('Video size'),
}),
type: 'readOnly',
},

handle: async (tab, params, response) => {
await tab.page.video().start({ size: params.size });
response.addTextResult('Video recording started.');
},
});

const stopVideo = defineTabTool({
capability: 'devtools',

schema: {
name: 'browser_stop_video',
title: 'Stop video',
description: 'Stop video recording',
inputSchema: z.object({
filename: z.string().optional().describe('Filename to save the video'),
}),
type: 'readOnly',
},

handle: async (tab, params, response) => {
const tmpPath = await tab.page.video().path();
let videoPath: string | undefined;
if (params.filename) {
const suggestedFilename = params.filename ?? dateAsFileName('video', 'webm');
videoPath = await tab.context.outputFile(suggestedFilename, { origin: 'llm', title: 'Saving video' });
}
await tab.page.video().stop({ path: videoPath });
response.addTextResult(`Video recording stopped: ${videoPath ?? tmpPath}`);
},
});

export default [
startVideo,
stopVideo,
];
5 changes: 3 additions & 2 deletions packages/playwright/src/mcp/config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export type ToolCapability =
'pdf' |
'storage' |
'testing' |
'tracing' |
'vision';
'vision' |
'devtools';

export type Config = {
/**
Expand Down Expand Up @@ -119,6 +119,7 @@ export type Config = {
* - 'core': Core browser automation features.
* - 'pdf': PDF generation and manipulation.
* - 'vision': Coordinate-based interactions.
* - 'devtools': Developer tools features.
*/
capabilities?: ToolCapability[];

Expand Down
8 changes: 6 additions & 2 deletions packages/playwright/src/mcp/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function decorateCommand(command: Command, version: string) {
.option('--blocked-origins <origins>', 'semicolon-separated list of origins to block the browser from requesting. Blocklist is evaluated before allowlist. If used without the allowlist, requests not matching the blocklist are still allowed.\nImportant: *does not* serve as a security boundary and *does not* affect redirects.', semicolonSeparatedList)
.option('--block-service-workers', 'block service workers')
.option('--browser <browser>', 'browser or chrome channel to use, possible values: chrome, firefox, webkit, msedge.')
.option('--caps <caps>', 'comma-separated list of additional capabilities to enable, possible values: vision, pdf.', commaSeparatedList)
.option('--caps <caps>', 'comma-separated list of additional capabilities to enable, possible values: vision, pdf, devtools.', commaSeparatedList)
.option('--cdp-endpoint <endpoint>', 'CDP endpoint to connect to.')
.option('--cdp-header <headers...>', 'CDP headers to send with the connect request, multiple can be specified.', headerParser)
.option('--codegen <lang>', 'specify the language to use for code generation, possible values: "typescript", "none". Default is "typescript".', enumParser.bind(null, '--codegen', ['none', 'typescript']))
Expand Down Expand Up @@ -80,6 +80,7 @@ export function decorateCommand(command: Command, version: string) {
.addOption(new ProgramOption('--daemon <socket>', 'run as daemon').hideHelp())
.addOption(new ProgramOption('--daemon-data-dir <path>', 'path to the daemon data directory.').hideHelp())
.addOption(new ProgramOption('--daemon-headed', 'run daemon in headed mode').hideHelp())
.addOption(new ProgramOption('--daemon-version <version>', 'version of this daemon').hideHelp())
.action(async options => {

// normalize the --no-sandbox option: sandbox = true => nothing was passed, sandbox = false => --no-sandbox was passed.
Expand All @@ -92,6 +93,9 @@ export function decorateCommand(command: Command, version: string) {
options.caps = 'vision';
}

if (options.caps?.includes('tracing'))
options.caps.push('devtools');

const config = await resolveCLIConfig(options);

// Chromium browsers require ffmpeg to be installed to save video.
Expand All @@ -114,7 +118,7 @@ export function decorateCommand(command: Command, version: string) {
version,
create: () => new BrowserServerBackend(config, contextFactory, { allTools: true, structuredOutput: true })
};
const socketPath = await startMcpDaemonServer(options.daemon, serverBackendFactory);
const socketPath = await startMcpDaemonServer(options.daemon, serverBackendFactory, options.daemonVersion);
console.error(`Daemon server listening on ${socketPath}`);
return;
}
Expand Down
Loading
Loading