From 45506fbff087294b7221b59b9806b0e90909db6f Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Sun, 25 Jan 2026 15:27:03 -0600 Subject: [PATCH] Fix: set device does not apply deviceScaleFactor - HiDPI screenshots not possible (#270) Fixes #255 --- src/actions.ts | 19 +++++++++++++++++++ src/browser.ts | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/actions.ts b/src/actions.ts index 33c51cb..a8402f3 100644 --- a/src/actions.ts +++ b/src/actions.ts @@ -1147,10 +1147,29 @@ async function handleDevice(command: DeviceCommand, browser: BrowserManager): Pr // Apply device viewport await browser.setViewport(device.viewport.width, device.viewport.height); + // Apply or clear device scale factor + if (device.deviceScaleFactor && device.deviceScaleFactor !== 1) { + // Apply device scale factor for HiDPI/retina displays + await browser.setDeviceScaleFactor( + device.deviceScaleFactor, + device.viewport.width, + device.viewport.height, + device.isMobile ?? false + ); + } else { + // Clear device scale factor override to restore default (1x) + try { + await browser.clearDeviceMetricsOverride(); + } catch { + // Ignore error if override was never set + } + } + return successResponse(command.id, { device: command.device, viewport: device.viewport, userAgent: device.userAgent, + deviceScaleFactor: device.deviceScaleFactor, }); } diff --git a/src/browser.ts b/src/browser.ts index 04089b2..00b3bd2 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -393,6 +393,40 @@ export class BrowserManager { await page.setViewportSize({ width, height }); } + /** + * Set device scale factor (devicePixelRatio) via CDP + * This sets window.devicePixelRatio which affects how the page renders and responds to media queries + * + * Note: When using CDP to set deviceScaleFactor, screenshots will be at logical pixel dimensions + * (viewport size), not physical pixel dimensions (viewport × scale). This is a Playwright limitation + * when using CDP emulation on existing contexts. For true HiDPI screenshots with physical pixels, + * deviceScaleFactor must be set at context creation time. + * + * Must be called after setViewport to work correctly + */ + async setDeviceScaleFactor( + deviceScaleFactor: number, + width: number, + height: number, + mobile: boolean = false + ): Promise { + const cdp = await this.getCDPSession(); + await cdp.send('Emulation.setDeviceMetricsOverride', { + width, + height, + deviceScaleFactor, + mobile, + }); + } + + /** + * Clear device metrics override to restore default devicePixelRatio + */ + async clearDeviceMetricsOverride(): Promise { + const cdp = await this.getCDPSession(); + await cdp.send('Emulation.clearDeviceMetricsOverride'); + } + /** * Get device descriptor */