Skip to content

Commit 529f767

Browse files
authored
fix(chromium): use actual device scale factor for css-scale screenshots (microsoft#39233)
1 parent 83d5089 commit 529f767

2 files changed

Lines changed: 19 additions & 5 deletions

File tree

packages/playwright-core/src/server/chromium/crPage.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ export class CRPage implements PageDelegate {
246246
}
247247

248248
async takeScreenshot(progress: Progress, format: 'png' | 'jpeg', documentRect: types.Rect | undefined, viewportRect: types.Rect | undefined, quality: number | undefined, fitsViewport: boolean, scale: 'css' | 'device'): Promise<Buffer> {
249-
const { visualViewport } = await progress.race(this._mainFrameSession._client.send('Page.getLayoutMetrics'));
249+
const { visualViewport, contentSize, cssContentSize } = await progress.race(this._mainFrameSession._client.send('Page.getLayoutMetrics'));
250250
if (!documentRect) {
251251
documentRect = {
252252
x: visualViewport.pageX + viewportRect!.x,
@@ -261,7 +261,9 @@ export class CRPage implements PageDelegate {
261261
// ignore current page scale.
262262
const clip = { ...documentRect, scale: viewportRect ? visualViewport.scale : 1 };
263263
if (scale === 'css') {
264-
const deviceScaleFactor = this._browserContext._options.deviceScaleFactor || 1;
264+
// deviceScaleFactor override does not affect layout metrics, so if it is set,
265+
// we use its value rather than computed one.
266+
const deviceScaleFactor = this._mainFrameSession._metricsOverride?.deviceScaleFactor || contentSize.width / cssContentSize.width || 1;
265267
clip.scale /= deviceScaleFactor;
266268
}
267269
const result = await progress.race(this._mainFrameSession._client.send('Page.captureScreenshot', { format, quality, clip, captureBeyondViewport: !fitsViewport }));
@@ -374,7 +376,7 @@ class FrameSession {
374376
// Marks the oopif session that remote -> local transition has happened in the parent.
375377
// See Target.detachedFromTarget handler for details.
376378
private _swappedIn = false;
377-
private _metricsOverride: Protocol.Emulation.setDeviceMetricsOverrideParameters | undefined;
379+
_metricsOverride: Protocol.Emulation.setDeviceMetricsOverrideParameters | undefined;
378380
private _workerSessions = new Map<string, CRSession>();
379381
private _initScriptIds = new Map<InitScript, string>();
380382
private _bufferedAttachedToTargetEvents: Protocol.Target.attachedToTargetPayload[] | undefined;

tests/library/screenshot.spec.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { verifyViewport } from '../config/utils';
2222
browserTest.describe('page screenshot', () => {
2323
browserTest.skip(({ browserName, headless }) => browserName === 'firefox' && !headless, 'Firefox headed produces a different image.');
2424

25-
browserTest('should run in parallel in multiple pages', async ({ server, contextFactory, browserName, isHeadlessShell, channel }) => {
25+
browserTest('should run in parallel in multiple pages', async ({ server, contextFactory }) => {
2626
const context = await contextFactory();
2727
const N = 5;
2828
const pages = await Promise.all(Array(N).fill(0).map(async () => {
@@ -50,7 +50,7 @@ browserTest.describe('page screenshot', () => {
5050
await context.close();
5151
});
5252

53-
browserTest('should work with a mobile viewport and clip', async ({ browser, server, browserName, channel }) => {
53+
browserTest('should work with a mobile viewport and clip', async ({ browser, server, browserName }) => {
5454
browserTest.skip(browserName === 'firefox');
5555

5656
const context = await browser.newContext({ viewport: { width: 320, height: 480 }, isMobile: true });
@@ -99,6 +99,18 @@ browserTest.describe('page screenshot', () => {
9999
await context.close();
100100
});
101101

102+
browserTest('should produce screenshot of correct size with scale:css and null viewport', async ({ browser, server }) => {
103+
const context = await browser.newContext({ viewport: null });
104+
const page = await context.newPage();
105+
await page.goto(server.PREFIX + '/grid.html');
106+
const [innerWidth, innerHeight] = await page.evaluate(() => [window.innerWidth, window.innerHeight]);
107+
const screenshot = await page.screenshot({ scale: 'css' });
108+
const decoded = PNG.sync.read(screenshot);
109+
expect(decoded.width).toBe(innerWidth);
110+
expect(decoded.height).toBe(innerHeight);
111+
await context.close();
112+
});
113+
102114
browserTest('should work with device scale factor, clip and scale:css', async ({ browser, server }) => {
103115
const context = await browser.newContext({ viewport: { width: 500, height: 500 }, deviceScaleFactor: 3 });
104116
const page = await context.newPage();

0 commit comments

Comments
 (0)