Multiple rapid touches in Electron's <webview> cause input freeze

Working around <webview> touch event problems.

Problem

When a view is loaded using the Electron's <webview> inside a BrowserWindow, touch events sometimes cease to be handled.

Views loaded in the <webview> become unresponsive for no discernible reason. We have identified that multiple rapid touches and more than 4 or 5 hours of idle state of the view loaded in <webview> trigger this issue.

This affects only touches. Mouse and keyboard events are unaffected.

Solution

Instead of creating a separate index.html file to load the view inside the BrowserWindow we can load the content.html file with the BrowserView's option webcontents.loadURL() and we can specify the preload script using the BrowserView's optional webPreferences object property.

app.on("ready", () => {
  const windowOptions = {
    width: 1280,
    height: 1024,
    resizable: false,
    autoHideMenuBar: true,
    webPreferences: {
      webSecurity: false
    }
  }

  const view = new BrowserView({
    webPreferences: {
      nodeIntegration: false,
      preload: path.join(__dirname, "preload.js")
    }
  })

  const mainWindow = new BrowserWindow(windowOptions)
  mainWindow.setBrowserView(view)
  view.setBounds({ x: 0, y: 0, width: 1280, height: 1024 })
  view.webContents.loadURL(`file://${__dirname}/content.html`)
})

Technical Description

As mentioned in the Electron Documentation, Chromium's <webview> is undergoing dramatic architectural changes. This impacts the stability of <webviews>, including rendering, navigation, and event routing.

As Electron simply utilises Chromium's <webview>, any issues with it will simply appear in Electron and cannot be controller by the Electron team.

A BrowserView can be utilised to embed additional web contents into BrowserWindow. Please refer to the example to transform <webview> content into BrowserView.

<webview> Code

The following file loads content.html and preload.js that will be loaded before other scripts run.

index.html
  <body>
    <webview id="webview" src="./content.html" preload="./preload.js"></webview>
    <script>
      const electron = require("electron")
      const webview = document.querySelector("webview")
      electron.ipcRenderer.on("changeScreen", function (event, view) {
          webview.loadURL(view, { "extraHeaders": "pragma: no-cache\n" })
      })
    </script>
  </body>

We were loading above index.html file inside the BrowserWindow with the following snippet.

index.ts
app.on("ready", () => {
  const windowOptions = {
    width: 1280,
    height: 1024,
    resizable: false,
    autoHideMenuBar: true,
    webPreferences: {
      webSecurity: true
    }
  }
  
  const mainWindow = new BrowserWindow(windowOptions)
  mainWindow.loadURL(`file://${__dirname}/index.html`)
})

Last updated