Search code examples
javascriptreactjswebviewelectronchromium

Electron webview's setWindowOpenHandler is not called at all when opening a new window


My Electron (v22.3.6) app uses <webview> tag.

This webview is located in the 'main.html' renderer process with <webview> tag.

And the 'main.html' is loaded by mainWindow, a BrowserWindow variable, in the main process.

Snippets of the code:

main.html

<webview id="wv" src="inner.html"></webview>

inner.html

<a href="https://example.com/" target="_blank">New window link inside inner.html</a>

main.js

let winOpts = {
    ...
    webPreferences: {
        plugins: false,
        nodeIntegration: false,
        nodeIntegrationInWorker: false,
        webviewTag: true,
        sandbox: false, // to use some components
        enableRemoteModule: false,
        contextIsolation: true,
        disableBlinkFeatures: "Auxclick",
        webSecurity: true,
        preload: "preload.js"
    }    
};

mainWindow = new BrowserWindow(winOpts);
mainWindow.webContents.setWindowOpenHandler((handler) => {
    //NOT CALLED AT ALL
    return {
        action: "allow"
    };
});

mainWindow.loadFile("main.html");

Problem:

When I click the link inside the 'inner.html', <a href="newsite" target="_blank">link</a>, nothing happens. Even 'console' in DevTools shows nothing. The 'click' behavior seems to trigger nothing.

Scheme

In the above figure, clicking the "Main Link" successfully calls setWindowOpenHandler. clicking the "Inner Link" does nothing. Neither opening a new window nor calling setWindowOpenHandler.

To sum up, "_blank" target link is not working inside <webview> which is in a BrowserWindow. setWindowOpenHandler is not called.


Solution

  • Found out how to:

    1. <webview> should be with allowpopups like <webview allowpopups></webview>

    2. In the 'main process', there should be a listener for newly generated webContents. And, attach the setWindowOpenHandler to the webview's webContents in that listener function.

        app.on('web-contents-created', (e, wc) => {
            // wc: webContents of <webview> is now under control
            wc.setWindowOpenHandler((handler) => {
                return {action : "deny"}; // deny or allow
            });
        });
    

    Explanation:

    1. setWindowOpenHandler only works with webContents.
    2. webview's webContents should be accessed from the main process.
    3. there is no direct way to access the webContents in renderer processes, and no need to use preload scripts for this.
    4. app.on('web-contents-created') can detect new webContents when a new webview is generated in the renderer process, and the very webContents is responsible for monitoring new window links (target="_blank" or window.open).

    No need for any security-vulnerable settings.