fix(pages): revert reporting the page size after resize_page (#3308)

This reverts commit f0030564a3 (#3132).

`McpPage.resize()` read `Page.getLayoutMetrics` from the target page
after the resize. Chrome does not lay out a background tab again until
the tab is visible. Thus, for a background tab, `resize_page` reported
the old page size (for example `Requested 1100x750, page is 1504x836`),
although the window had the requested size.

The window bounds (`Browser.getWindowBounds`) cannot replace the layout
metrics. They give the outer window size, which includes the tab strip
and the toolbar, not the page size.

`resize_page` again prints no page size. #2998 is open again.

Fixes #3306
This commit is contained in:
Alex Rudenko authored and GitHub committed 2026-10-22 09:23:54 +00:00
1 parent ecab31def0
commit 186ea66f1b
7 files changed
+22 -236

No files matched your search

+1 -1
View File
@@ -298,7 +298,7 @@
### `resize_page`
**Description:** Resizes the page's window so that the page has specified dimension. If the browser clamps the window size, the actual page dimensions are reported.
**Description:** Resizes the page's window so that the page has specified dimension
**Parameters:**
-39
View File
@@ -1439,45 +1439,6 @@ export class McpPage implements ContextPage {
});
}
async resize(
width: number,
height: number,
): Promise<{width: number; height: number} | undefined> {
try {
const browser = this.pptrPage.browser();
const windowId = await this.pptrPage.windowId();
const bounds = await browser.getWindowBounds(windowId);
if (bounds.windowState === 'fullscreen') {
// Have to call this twice on Ubuntu when the window is in fullscreen mode.
await browser.setWindowBounds(windowId, {windowState: 'normal'});
await browser.setWindowBounds(windowId, {windowState: 'normal'});
} else if (bounds.windowState !== 'normal') {
await browser.setWindowBounds(windowId, {windowState: 'normal'});
}
} catch {
// Window APIs are not supported on all platforms
}
await this.pptrPage.resize({
contentWidth: width,
contentHeight: height,
});
if (this.getDialog()) {
return undefined;
}
// @ts-expect-error internal API
const client = this.pptrPage._client();
const metrics: Protocol.Page.GetLayoutMetricsResponse = await client.send(
'Page.getLayoutMetrics',
);
return {
width: metrics.cssLayoutViewport.clientWidth,
height: metrics.cssLayoutViewport.clientHeight,
};
}
lighthouseNavigation(flags?: Flags): Promise<RunnerResult | undefined> {
return lighthouseNavigation(this.pptrPage, this.pptrPage.url(), {
flags,
+1 -1
View File
@@ -1754,7 +1754,7 @@ export const commands: Commands = {
},
resize_page: {
description:
"Resizes the page's window so that the page has specified dimension. If the browser clamps the window size, the actual page dimensions are reported.",
"Resizes the page's window so that the page has specified dimension",
category: 'Emulation',
args: {
pageId: {
-4
View File
@@ -529,10 +529,6 @@ export type ContextPage = Readonly<{
text: string[],
options?: {timeout?: number; visible?: boolean},
): Promise<Element>;
resize(
width: number,
height: number,
): Promise<{width: number; height: number} | undefined>;
getDevToolsPage(): Promise<Page | undefined>;
openDevTools(): Promise<Page | undefined>;
ensureDevToolsCommentBridge(
+20 -10
View File
@@ -355,7 +355,7 @@ export const navigatePage: PageToolFactory = definePageTool(
export const resizePage: PageToolFactory = definePageTool(() => ({
name: 'resize_page',
description: `Resizes the page's window so that the page has specified dimension. If the browser clamps the window size, the actual page dimensions are reported.`,
description: `Resizes the page's window so that the page has specified dimension`,
annotations: {
category: ToolCategory.EMULATION,
readOnlyHint: false,
@@ -369,17 +369,27 @@ export const resizePage: PageToolFactory = definePageTool(() => ({
verifyFilesSchema: {},
handler: async (request, response) => {
const page = request.page;
const {width, height} = request.params;
const actualSize = await page.resize(width, height);
if (
actualSize &&
(actualSize.width !== width || actualSize.height !== height)
) {
response.appendResponseLine(
`Requested ${width}x${height}, page is ${actualSize.width}x${actualSize.height}`,
);
try {
const browser = page.pptrPage.browser();
const windowId = await page.pptrPage.windowId();
const bounds = await browser.getWindowBounds(windowId);
if (bounds.windowState === 'fullscreen') {
// Have to call this twice on Ubuntu when the window is in fullscreen mode.
await browser.setWindowBounds(windowId, {windowState: 'normal'});
await browser.setWindowBounds(windowId, {windowState: 'normal'});
} else if (bounds.windowState !== 'normal') {
await browser.setWindowBounds(windowId, {windowState: 'normal'});
}
} catch {
// Window APIs are not supported on all platforms
}
await page.pptrPage.resize({
contentWidth: request.params.width,
contentHeight: request.params.height,
});
response.setIncludePages(true);
},
-102
View File
@@ -26,7 +26,6 @@ import {
createMockCDPSession,
createMockDialog,
createMockMcpResponse,
createMockPuppeteerBrowser,
createMockPuppeteerPage,
createMockPuppeteerTarget,
createMockServerConfig,
@@ -2258,107 +2257,6 @@ describe('McpPage', () => {
});
});
describe('resize()', () => {
it('resizes the page and returns the resulting cssLayoutViewport dimensions', async () => {
const {mcpPage, pptrPage} = await createMcpPage();
const browser = createMockPuppeteerBrowser();
pptrPage.browser.returns(browser);
pptrPage.windowId.resolves('win-1');
browser.getWindowBounds.resolves({windowState: 'normal'});
pptrPage.cdpSession.send.withArgs('Page.getLayoutMetrics').resolves({
cssLayoutViewport: {
clientWidth: 500,
clientHeight: 232,
pageX: 0,
pageY: 0,
},
});
const actualSize = await mcpPage.resize(100, 100);
sinon.assert.notCalled(browser.setWindowBounds);
sinon.assert.calledOnceWithExactly(pptrPage.resize, {
contentWidth: 100,
contentHeight: 100,
});
assert.deepStrictEqual(actualSize, {width: 500, height: 232});
});
it('restores normal window state from fullscreen twice before resizing', async () => {
const {mcpPage, pptrPage} = await createMcpPage();
const browser = createMockPuppeteerBrowser();
pptrPage.browser.returns(browser);
pptrPage.windowId.resolves('win-1');
browser.getWindowBounds.resolves({windowState: 'fullscreen'});
pptrPage.cdpSession.send.withArgs('Page.getLayoutMetrics').resolves({
cssLayoutViewport: {
clientWidth: 800,
clientHeight: 600,
pageX: 0,
pageY: 0,
},
});
const actualSize = await mcpPage.resize(800, 600);
sinon.assert.calledTwice(browser.setWindowBounds);
sinon.assert.calledWithExactly(
browser.setWindowBounds.firstCall,
'win-1',
{windowState: 'normal'},
);
sinon.assert.calledWithExactly(
browser.setWindowBounds.secondCall,
'win-1',
{windowState: 'normal'},
);
sinon.assert.calledOnceWithExactly(pptrPage.resize, {
contentWidth: 800,
contentHeight: 600,
});
assert.deepStrictEqual(actualSize, {width: 800, height: 600});
});
it('continues resizing when window bounds APIs are unsupported', async () => {
const {mcpPage, pptrPage} = await createMcpPage();
pptrPage.windowId.rejects(new Error('Window APIs not supported'));
pptrPage.cdpSession.send.withArgs('Page.getLayoutMetrics').resolves({
cssLayoutViewport: {
clientWidth: 700,
clientHeight: 500,
pageX: 0,
pageY: 0,
},
});
const actualSize = await mcpPage.resize(700, 500);
sinon.assert.calledOnceWithExactly(pptrPage.resize, {
contentWidth: 700,
contentHeight: 500,
});
assert.deepStrictEqual(actualSize, {width: 700, height: 500});
});
it('returns undefined without reading layout metrics when a dialog is open', async () => {
const {mcpPage, pptrPage} = await createMcpPage();
pptrPage.windowId.rejects(new Error('Window APIs not supported'));
pptrPage.emit('dialog', createMockDialog());
const actualSize = await mcpPage.resize(700, 500);
sinon.assert.calledOnceWithExactly(pptrPage.resize, {
contentWidth: 700,
contentHeight: 500,
});
sinon.assert.neverCalledWith(
pptrPage.cdpSession.send,
'Page.getLayoutMetrics',
);
assert.strictEqual(actualSize, undefined);
});
});
describe('dialogs', () => {
it('returns the dialog and throws when unhandled', async () => {
const {mcpPage, pptrPage} = await createMcpPage();
-79
View File
@@ -1537,85 +1537,6 @@ describe('pages', () => {
});
});
it('reports actual page dimensions when the browser clamps width or height', async () => {
const {page, response, context, args} = createHandlerMocks();
page.resize.resolves({width: 500, height: 100});
await resizePage(args).handler(
{
params: {width: 100, height: 100},
page,
},
response,
context,
);
sinon.assert.calledOnceWithExactly(page.resize, 100, 100);
sinon.assert.calledOnceWithExactly(
response.appendResponseLine,
'Requested 100x100, page is 500x100',
);
sinon.assert.calledOnceWithExactly(response.setIncludePages, true);
const heightMocks = createHandlerMocks();
heightMocks.page.resize.resolves({width: 800, height: 232});
await resizePage(heightMocks.args).handler(
{
params: {width: 800, height: 100},
page: heightMocks.page,
},
heightMocks.response,
heightMocks.context,
);
sinon.assert.calledOnceWithExactly(heightMocks.page.resize, 800, 100);
sinon.assert.calledOnceWithExactly(
heightMocks.response.appendResponseLine,
'Requested 800x100, page is 800x232',
);
sinon.assert.calledOnceWithExactly(
heightMocks.response.setIncludePages,
true,
);
});
it('does not append a size warning when the page matches requested dimensions', async () => {
const {page, response, context, args} = createHandlerMocks();
page.resize.resolves({width: 700, height: 500});
await resizePage(args).handler(
{
params: {width: 700, height: 500},
page,
},
response,
context,
);
sinon.assert.calledOnceWithExactly(page.resize, 700, 500);
sinon.assert.notCalled(response.appendResponseLine);
sinon.assert.calledOnceWithExactly(response.setIncludePages, true);
});
it('does not append a size line when the actual page size is unknown', async () => {
const {page, response, context, args} = createHandlerMocks();
page.resize.resolves(undefined);
await resizePage(args).handler(
{
params: {width: 700, height: 500},
page,
},
response,
context,
);
sinon.assert.calledOnceWithExactly(page.resize, 700, 500);
sinon.assert.notCalled(response.appendResponseLine);
sinon.assert.calledOnceWithExactly(response.setIncludePages, true);
});
it('resize when window state is normal', async () => {
await withMcpContext(async (response, context, args) => {
const page = context.getSelectedMcpPage().pptrPage;