From 07ad0ed41e59837457b871e062054f701c3ac940 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 May 2026 16:37:13 -0700 Subject: [PATCH 1/5] fix(mcp): skip base64 image response when `filename` is explicitly provided to `browser_take_screenshot` (#40577) --- packages/playwright-core/src/tools/backend/screenshot.ts | 3 ++- tests/mcp/screenshot.spec.ts | 5 ----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/playwright-core/src/tools/backend/screenshot.ts b/packages/playwright-core/src/tools/backend/screenshot.ts index 71342abc9e0c0..d2aeea504a2d3 100644 --- a/packages/playwright-core/src/tools/backend/screenshot.ts +++ b/packages/playwright-core/src/tools/backend/screenshot.ts @@ -67,7 +67,8 @@ const screenshot = defineTabTool({ response.addCode(`await page.screenshot(${formatObject({ ...options, path: resolvedFile.relativeName })});`); await response.addFileResult(resolvedFile, data); - await response.registerImageResult(data, fileType); + if (!params.filename) + await response.registerImageResult(data, fileType); } }); diff --git a/tests/mcp/screenshot.spec.ts b/tests/mcp/screenshot.spec.ts index bbe3c74d7991c..faa0d7d5eec73 100644 --- a/tests/mcp/screenshot.spec.ts +++ b/tests/mcp/screenshot.spec.ts @@ -238,11 +238,6 @@ test('browser_take_screenshot (filename: "output.png")', async ({ client, server text: expect.stringContaining(`output.png`), type: 'text', }, - { - data: expect.any(String), - mimeType: 'image/png', - type: 'image', - }, ], }); From ed1650854c626f807dd8567f17be8b52343838d3 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Tue, 5 May 2026 16:50:33 -0700 Subject: [PATCH 2/5] docs: alias drop-payload Object types and dedupe identical aliases (#40651) --- docs/src/api/params.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/src/api/params.md b/docs/src/api/params.md index dc86942aa689c..350afc877b268 100644 --- a/docs/src/api/params.md +++ b/docs/src/api/params.md @@ -128,15 +128,16 @@ Defaults to `left`. ## input-files - `files` <[path]|[Array]<[path]>|[Object]|[Array]<[Object]>> - - alias-csharp: FilePayload - - alias-java: FilePayload + - alias: FilePayload - `name` <[string]> File name - `mimeType` <[string]> File type - `buffer` <[Buffer]> File content ## drop-payload - `payload` <[Object]> + - alias: DropPayload - `files` ?<[path]|[Array]<[path]>|[Object]|[Array]<[Object]>> + - alias: FilePayload - `name` <[string]> File name - `mimeType` <[string]> File type - `buffer` <[Buffer]> File content From 6eab8a3c2bae6c8ea472629f268544aa9fdc33a7 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Tue, 5 May 2026 17:56:18 -0700 Subject: [PATCH 3/5] test: forward NODE_OPTIONS to remote server child processes (#40653) --- tests/config/remoteServer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/config/remoteServer.ts b/tests/config/remoteServer.ts index c2989da4fde66..b541628da09c7 100644 --- a/tests/config/remoteServer.ts +++ b/tests/config/remoteServer.ts @@ -15,7 +15,6 @@ */ import path from 'path'; -import { inheritAndCleanEnv } from './utils'; import type { BrowserType, Browser } from 'playwright-core'; import type { CommonFixtures, TestChildProcess } from './commonFixtures'; @@ -36,7 +35,7 @@ export class RunServer implements PlaywrightServer { command.push(`--artifacts-dir=${options.artifactsDir}`); this._process = childProcess({ command, - env: inheritAndCleanEnv(options?.env), + env: { NODE_OPTIONS: process.env.NODE_OPTIONS, ...options?.env }, }); let wsEndpointCallback: (value: string) => void; @@ -108,6 +107,7 @@ export class RemoteServer implements PlaywrightServer { }; this._process = childProcess({ command: ['node', path.join(__dirname, 'remote-server-impl.js'), JSON.stringify(options)], + env: { NODE_OPTIONS: process.env.NODE_OPTIONS }, }); let index = 0; From f6deb2f071f7c8097186cca11cc409c0bc8f38a4 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Tue, 5 May 2026 20:17:39 -0700 Subject: [PATCH 4/5] fix(screencast): plumb viewport size through onFrame callback (#40649) --- docs/src/api/class-screencast.md | 8 +++++-- packages/playwright-client/types/types.d.ts | 6 +++-- .../playwright-core/src/client/screencast.ts | 8 +++---- .../playwright-core/src/protocol/validator.ts | 2 ++ .../src/server/dispatchers/pageDispatcher.ts | 2 +- .../tools/dashboard/dashboardController.ts | 5 ++-- packages/playwright-core/types/types.d.ts | 6 +++-- packages/protocol/spec/page.yml | 2 ++ packages/protocol/src/channels.d.ts | 2 ++ tests/library/screencast.spec.ts | 23 +++++++++++++++++++ utils/generate_types/overrides.d.ts | 2 +- 11 files changed, 51 insertions(+), 15 deletions(-) diff --git a/docs/src/api/class-screencast.md b/docs/src/api/class-screencast.md index 7b28daf8b1ff3..4d1ba1843ea8e 100644 --- a/docs/src/api/class-screencast.md +++ b/docs/src/api/class-screencast.md @@ -22,7 +22,9 @@ await page.screencast.stop(); ```js // Capture frames await page.screencast.start({ - onFrame: ({ data }) => console.log(`frame size: ${data.length}`), + onFrame: ({ data, viewportWidth, viewportHeight }) => { + console.log(`frame size: ${data.length} (${viewportWidth}x${viewportHeight})`); + }, size: { width: 800, height: 600 }, }); // ... perform actions ... @@ -34,8 +36,10 @@ await page.screencast.stop(); - `onFrame` <[function]\([Object]\): [Promise]> - alias: ScreencastFrame - `data` <[Buffer]> JPEG-encoded frame data. + - `viewportWidth` <[int]> Width of the page viewport at the time the frame was captured. + - `viewportHeight` <[int]> Height of the page viewport at the time the frame was captured. -Callback that receives JPEG-encoded frame data. +Callback that receives JPEG-encoded frame data along with the page viewport size at the time of capture. ### option: Screencast.start.path * since: v1.59 diff --git a/packages/playwright-client/types/types.d.ts b/packages/playwright-client/types/types.d.ts index 4a33756068ddb..db9b9739919a0 100644 --- a/packages/playwright-client/types/types.d.ts +++ b/packages/playwright-client/types/types.d.ts @@ -16557,7 +16557,9 @@ export interface Screencast { * ```js * // Capture frames * await page.screencast.start({ - * onFrame: ({ data }) => console.log(`frame size: ${data.length}`), + * onFrame: ({ data, viewportWidth, viewportHeight }) => { + * console.log(`frame size: ${data.length} (${viewportWidth}x${viewportHeight})`); + * }, * size: { width: 800, height: 600 }, * }); * // ... perform actions ... @@ -16567,7 +16569,7 @@ export interface Screencast { * @param options */ start(options?: { - onFrame?: (frame: { data: Buffer }) => Promise|any; + onFrame?: (frame: { data: Buffer, viewportWidth: number, viewportHeight: number }) => Promise|any; path?: string; size?: { width: number; diff --git a/packages/playwright-core/src/client/screencast.ts b/packages/playwright-core/src/client/screencast.ts index 83c21db702457..e4a775688e20d 100644 --- a/packages/playwright-core/src/client/screencast.ts +++ b/packages/playwright-core/src/client/screencast.ts @@ -24,17 +24,17 @@ export class Screencast implements api.Screencast { private _page: Page; private _started = false; private _savePath: string | undefined; - private _onFrame: ((frame: { data: Buffer }) => Promise) | null = null; + private _onFrame: ((frame: { data: Buffer, viewportWidth: number, viewportHeight: number }) => Promise) | null = null; private _artifact: Artifact | undefined; constructor(page: Page) { this._page = page; - this._page._channel.on('screencastFrame', ({ data }) => { - void this._onFrame?.({ data }); + this._page._channel.on('screencastFrame', ({ data, viewportWidth, viewportHeight }) => { + void this._onFrame?.({ data, viewportWidth, viewportHeight }); }); } - async start(options: { onFrame?: (frame: { data: Buffer }) => Promise|any, path?: string, size?: { width: number, height: number }, quality?: number } = {}): Promise { + async start(options: { onFrame?: (frame: { data: Buffer, viewportWidth: number, viewportHeight: number }) => Promise|any, path?: string, size?: { width: number, height: number }, quality?: number } = {}): Promise { if (this._started) throw new Error('Screencast is already started'); this._started = true; diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index 831dfc6f19d24..8c0a3fcb23b84 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -2189,6 +2189,8 @@ scheme.PageRouteEvent = tObject({ }); scheme.PageScreencastFrameEvent = tObject({ data: tBinary, + viewportWidth: tInt, + viewportHeight: tInt, }); scheme.PageWebSocketRouteEvent = tObject({ webSocketRoute: tChannel(['WebSocketRoute']), diff --git a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts index 867476914df60..56dc88c2cecf1 100644 --- a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts @@ -396,7 +396,7 @@ export class PageDispatcher extends Dispatcher { - this._dispatchEvent('screencastFrame', { data: frame.buffer }); + this._dispatchEvent('screencastFrame', { data: frame.buffer, viewportWidth: frame.viewportWidth, viewportHeight: frame.viewportHeight }); }, dispose: () => {}, size: params.size, diff --git a/packages/playwright-core/src/tools/dashboard/dashboardController.ts b/packages/playwright-core/src/tools/dashboard/dashboardController.ts index d67269d184e0a..79e0c503c5474 100644 --- a/packages/playwright-core/src/tools/dashboard/dashboardController.ts +++ b/packages/playwright-core/src/tools/dashboard/dashboardController.ts @@ -448,11 +448,10 @@ class AttachedPage { private async _startScreencast(page: api.Page) { await page.screencast.start({ - onFrame: ({ data }: { data: Buffer }) => { + onFrame: ({ data, viewportWidth, viewportHeight }) => { if (this._disposed) return; - const vp = page.viewportSize(); - this._owner.emitFrame(data.toString('base64'), vp?.width ?? 0, vp?.height ?? 0); + this._owner.emitFrame(data.toString('base64'), viewportWidth, viewportHeight); }, size: { width: 1280, height: 800 }, ...(this._recordingPath ? { path: this._recordingPath } : {}), diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 4a33756068ddb..db9b9739919a0 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -16557,7 +16557,9 @@ export interface Screencast { * ```js * // Capture frames * await page.screencast.start({ - * onFrame: ({ data }) => console.log(`frame size: ${data.length}`), + * onFrame: ({ data, viewportWidth, viewportHeight }) => { + * console.log(`frame size: ${data.length} (${viewportWidth}x${viewportHeight})`); + * }, * size: { width: 800, height: 600 }, * }); * // ... perform actions ... @@ -16567,7 +16569,7 @@ export interface Screencast { * @param options */ start(options?: { - onFrame?: (frame: { data: Buffer }) => Promise|any; + onFrame?: (frame: { data: Buffer, viewportWidth: number, viewportHeight: number }) => Promise|any; path?: string; size?: { width: number; diff --git a/packages/protocol/spec/page.yml b/packages/protocol/spec/page.yml index 4d9b1793b94ec..a5e6e16488e81 100644 --- a/packages/protocol/spec/page.yml +++ b/packages/protocol/spec/page.yml @@ -652,6 +652,8 @@ Page: screencastFrame: parameters: data: binary + viewportWidth: int + viewportHeight: int webSocketRoute: parameters: diff --git a/packages/protocol/src/channels.d.ts b/packages/protocol/src/channels.d.ts index edb828825bf8a..b1aede0f72880 100644 --- a/packages/protocol/src/channels.d.ts +++ b/packages/protocol/src/channels.d.ts @@ -3908,6 +3908,8 @@ export type PageRouteEvent = { }; export type PageScreencastFrameEvent = { data: Binary, + viewportWidth: number, + viewportHeight: number, }; export type PageWebSocketRouteEvent = { webSocketRoute: WebSocketRouteChannel, diff --git a/tests/library/screencast.spec.ts b/tests/library/screencast.spec.ts index 24e345ca0740d..867766fb2d163 100644 --- a/tests/library/screencast.spec.ts +++ b/tests/library/screencast.spec.ts @@ -52,6 +52,29 @@ test('screencast.start delivers frames via onFrame callback', async ({ browser, await context.close(); }); +test('onFrame receives viewport size', async ({ browser, server, trace }) => { + test.skip(trace === 'on', 'trace=on has different screencast image configuration'); + const context = await browser.newContext({ viewport: { width: 1000, height: 400 } }); + const page = await context.newPage(); + + const frames: { viewportWidth: number, viewportHeight: number }[] = []; + await page.screencast.start({ + onFrame: ({ viewportWidth, viewportHeight }) => frames.push({ viewportWidth, viewportHeight }), + size: { width: 500, height: 400 }, + }); + await page.goto(server.EMPTY_PAGE); + await ensureSomeFrames(page); + await page.screencast.stop(); + + expect(frames.length).toBeGreaterThan(0); + for (const frame of frames) { + expect(frame.viewportWidth).toBe(1000); + expect(frame.viewportHeight).toBe(400); + } + + await context.close(); +}); + test('start throws if screencast is already started', async ({ browser }) => { const context = await browser.newContext({ viewport: { width: 500, height: 400 } }); const page = await context.newPage(); diff --git a/utils/generate_types/overrides.d.ts b/utils/generate_types/overrides.d.ts index dba016b023342..f7f10b66a3b3b 100644 --- a/utils/generate_types/overrides.d.ts +++ b/utils/generate_types/overrides.d.ts @@ -245,7 +245,7 @@ export interface WebSocketRoute { export interface Screencast { start(options?: { - onFrame?: (frame: { data: Buffer }) => Promise|any; + onFrame?: (frame: { data: Buffer, viewportWidth: number, viewportHeight: number }) => Promise|any; path?: string; size?: { width: number; From 9eeb4cc5bf529825e510b3062348bfa2b4746bab Mon Sep 17 00:00:00 2001 From: "microsoft-playwright-automation[bot]" <203992400+microsoft-playwright-automation[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 20:27:04 -0700 Subject: [PATCH 5/5] feat(chromium-tip-of-tree): roll to r1427 (#40647) Co-authored-by: microsoft-playwright-automation[bot] <203992400+microsoft-playwright-automation[bot]@users.noreply.github.com> --- packages/playwright-core/browsers.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/playwright-core/browsers.json b/packages/playwright-core/browsers.json index 14b371e0d4806..35841787b39c2 100644 --- a/packages/playwright-core/browsers.json +++ b/packages/playwright-core/browsers.json @@ -17,16 +17,16 @@ }, { "name": "chromium-tip-of-tree", - "revision": "1426", + "revision": "1427", "installByDefault": false, - "browserVersion": "149.0.7816.0", + "browserVersion": "149.0.7827.0", "title": "Chrome Canary for Testing" }, { "name": "chromium-tip-of-tree-headless-shell", - "revision": "1426", + "revision": "1427", "installByDefault": false, - "browserVersion": "149.0.7816.0", + "browserVersion": "149.0.7827.0", "title": "Chrome Canary Headless Shell" }, {