Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to trigger standard zoom (not headless)

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.


My current solution

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?

like image 756
Steve H Avatar asked Sep 05 '19 09:09

Steve H


People also ask

How do you set a headless false puppeteer?

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.

Does puppeteer need Chrome installed?

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.


2 Answers

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.

like image 110
mihi Avatar answered Oct 19 '22 11:10

mihi


I just use Javascript to help zoom-in / zoom-out.

await page.evaluate(() => document.body.style.zoom = 0.5  );

It works well for me.

like image 43
user18696898 Avatar answered Oct 19 '22 12:10

user18696898