I am running puppeteer with headless mode off in order to automate and remotely control a visible Chromium browser on another computer.
Is there a way to trigger or emulate zooming on the browser just like in the UI menus or ctrl +
/crtl -
commands?
Injecting CSS or using the various documented scale commands does not fully replicate this as, for example, elements defined using vh
/vw
units do not get adjusted.
Using viewport scale in Emulation.setDeviceMetricsOverride
works well for zooming out but it seems to be resizing a raster of the page rather than rendering at the target size, resulting in blurry text when zooming in.
Adjusting the viewport size and using Emulation.setPageScaleFactor
works well for zooming in however a pageScaleFactor of less than 1 seems to be ignored in my testing.
An issue with both of these solutions is that it requires knowing the width/height of the browser window in advance and relies on that not changing, rather than having a fluid viewport. I'm also not sure what other features of the standard browser zoom I am missing.
My code for zooming is now:
async applyFrameZoom(page, zoom) {
// page is a puppeteer.Page instance
// zoom is an integer percentage
const session = await page.target().createCDPSession();
let window = await session.send('Browser.getWindowForTarget', {
targetId: page.target()._targetId
});
let width = window.bounds.width;
let height = window.bounds.height;
if (!zoom || zoom === 100) {
// Unset any zoom
await session.send('Emulation.clearDeviceMetricsOverride');
await session.send('Emulation.resetPageScaleFactor');
} else if (zoom > 100) {
// Unset other zooming method
await session.send('Emulation.clearDeviceMetricsOverride');
// Zoom in by reducing size then adjusting page scale (unable to zoom out using this method)
await page.setViewport({
width: Math.round(width / (zoom / 100)),
height: Math.round(height / (zoom / 100))
});
await session.send('Emulation.setPageScaleFactor', {
pageScaleFactor: (zoom / 100)
});
await session.send('Emulation.setVisibleSize', {
width: width,
height: height
});
} else {
// Unset other zooming method
await session.send('Emulation.resetPageScaleFactor');
// Zoom out by emulating a scaled device (makes text blurry when zooming in with this method)
await session.send('Emulation.setDeviceMetricsOverride', {
width: Math.round(width / (zoom / 100)),
height: Math.round(height / (zoom / 100)),
mobile: false,
deviceScaleFactor: 1,
dontSetVisibleSize: true,
viewport: {
x: 0,
y: 0,
width: width,
height: height,
scale: (zoom / 100)
}
});
}
await this.frame.waitForSelector('html');
this.frame.evaluate(function () {
window.dispatchEvent(new Event('resize'));
});
}
Is there a better way to do this?
Auto-Open DevTools Panel Once again, inside the initial command launching Puppeteer, add in puppeteer. launch({headless: false, devtools: true}); to open a DevTools panel. Note: If this option is true , the headless option will be set false . You can even console.
By default, Puppeteer downloads and uses a specific version of Chromium so its API is guaranteed to work out of the box. To use Puppeteer with a different version of Chrome or Chromium, pass in the executable's path when creating a Browser instance: const browser = await puppeteer.
The --force-device-scale-factor
command line option seems to work for scaling the complete chrome UI, both for zooming in and zooming out.
Passing that to chrome with puppeteer:
puppeteer.launch({
args: ["--force-device-scale-factor=0.5"],
headless: false,
})
(Tested with chromium 78 / puppeteer 1.20.0)
But if you need to zoom without restarting chrome or don't want to scale the whole UI, there actually is a way to trigger the native chrome zoom using puppeteer.
I've created a repository demonstrating that here. It works by taking a detour through a chrome extension, which has access to the chrome.tabs.setZoom API.
chrome-extension/manifest.json
:
{
"name": "my-extension-name",
"description": "A minimal chrome extension to help puppeteer with zooming",
"version": "1.0",
"manifest_version": 2,
"background": {
"scripts": ["background.js"],
"persistent": false
},
"permissions": []
}
chrome-extension/background.js
:
function setZoom(tabId, zoomFactor) {
chrome.tabs.setZoom(tabId, zoomFactor)
}
main.js
:
const puppeteer = require('puppeteer');
(async () => {
const extensionPath = require('path').join(__dirname, 'chrome-extension');
const extensionName = 'my-extension-name';
const browser = await puppeteer.launch({
args: [
`--disable-extensions-except=${extensionPath}`,
`--load-extension=${extensionPath}`
],
headless: false,
});
const page = await browser.newPage();
await page.goto('https://google.com');
// get the background page of the extension
const targets = await browser.targets();
const extenstionPageTarget = targets.find(
(target) => target._targetInfo.title === extensionName
);
const extensionPage = await extenstionPageTarget.page();
// do the zooming by invoking setZoom of background.js
const zoomFactor = 0.5
await extensionPage.evaluate((zoomFactor) => {
// a tabId of undefined defaults to the currently active tab
const tabId = undefined;
setZoom(tabId, zoomFactor);
}, zoomFactor);
})();
I haven't found a way of getting the tabId
of a page using puppeteer, although that is probably again possible through the extension.
But the above will do if the page you want to zoom is the currently active one.
Note that loading chrome extensions does not currently work in headless mode, but this is luckily not a problem in your case.
I just use Javascript to help zoom-in / zoom-out.
await page.evaluate(() => document.body.style.zoom = 0.5 );
It works well for me.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With