Search code examples
javascriptnode.jselectronchromium

Electron.js: how to create a separate download webContents.session for a window?


I have an Electron file manager app, which creates 2 windows for different purposes:

  • quickView a renderer window used for previewing local files. It uses "will-download" listener for detecting unsupported files by preventing download.

  • main the main renderer window. It uses "will-download" listener for downloading files.

Each with their own will-download listeners attached to their session. But for some reason the quickView listener overwrites the main listener.

Window 1

In the following line I'm creating a will-download listener for the "main" process. The purpose of this listener is to download files:

https://github.com/aleksey-hoffman/sigma-file-manager/blob/55fd2462cf83898610883191807b7488fb5bdf89/src/utils/downloadManager.js#L133

win.webContents.session.on('will-download', listener)

The windows.main parameter in the line below is the win reference in the line above:

https://github.com/aleksey-hoffman/sigma-file-manager/blob/55fd2462cf83898610883191807b7488fb5bdf89/src/electronMain.js#L516

const resultInfo = await downloadManager.download(windows.main, {

Window 2

In the following line I'm creating a will-download listener for the "quickView" window. The purpose of this listener is to detect unsupported files (which triggers download event in Chromium) and prevent the download event:

https://github.com/aleksey-hoffman/sigma-file-manager/blob/55fd2462cf83898610883191807b7488fb5bdf89/src/electronMain.js#L232

windows.quickViewWindow.webContents.session.once('will-download', _willDownloadHandler)

I haven't found another way to detect unsupported files, which is why I'm using a will-download event in the first place.

Problem

For some reason the will-download handler of the quickView window overrides the handler of the main:

When I trigger the app update download event here (from the main process):

https://github.com/aleksey-hoffman/sigma-file-manager/blob/55fd2462cf83898610883191807b7488fb5bdf89/src/electronMain.js#L516

const resultInfo = await downloadManager.download(windows.main, {

It triggers the event handler of the quickView renderer window:

https://github.com/aleksey-hoffman/sigma-file-manager/blob/55fd2462cf83898610883191807b7488fb5bdf89/src/electronMain.js#L241

function _willDownloadHandler (event, item, webContents) {
  ...
  windows.main.webContents.send('load:webview::failed', {path: fileURL})

Partial fix

I partially fixed the problem in this commit by specifying a custom partition name for the session of the quickView window, so it doesn't use the default session and do not overwrite the will-download listener created by main:

Main process:

windows.quickViewWindow = new electron.BrowserWindow({
  ...
  webPreferences: {
    partition: 'quickPreview',

...

windows.quickViewWindow.webContents.session.once(
  'will-download',
  (event, item, webContents) => {
    event.preventDefault()
    ...
  }
)

quickViewWindow.html:

ipcRenderer.on('load:webview', (event, data) => {
  ...
  webviewNode.setAttribute('partition', 'quickPreview')

But this fix resulted in another problem:

  • The quick view window stopped working in production build (perhaps it has something to do with the protocol not working with non default session partition)

  • Setting a custom partition to a webview causes the Windows protocol link association pop up in production when the window containing this webview is created:

image

I think it might be caused by the custom app:// protocol created by the electron-builder-plugin. It seems the pop up is triggered by the "app" link.

Or maybe it's happening because I'm setting the protocol incorrectly when I'm creating the window somewhere around this line:

https://github.com/aleksey-hoffman/sigma-file-manager/blob/47ce65bdac78e5c9b17315f16623d40d81dcf1bb/src/electronMain.js#L203

To reproduce:

  1. Download the project
git clone https://github.com/aleksey-hoffman/sigma-file-manager.git
cd sigma-file-manager
npm install
git checkout 47ce65b
npm run electron:build
  1. Install the built app from ./dist_electron
  2. During the app launch you can see the pop up

Notes:

I just rolled back the 47ce65b commit and added some test values so it's easier to debug

To switch to the latest commit and create the production build:

git checkout 5246252
npm run electron:build

All the console.log() inside electronMain.js are displayed in the terminal (command line) window (not the developer tools console).

To trigger quick view feature:

  • Select any supported file (image/ text / etc) on the navigator page
  • Press Space (the quick view window should open)

To trigger a download event, you can just open "Navigator" page and drag & drop any file (or website URL) from the internet. It will trigger the wrong will-download event handler (the handler of quickView window), and you should see the console message.

The quickView window containing this webview is created on app.ready event. When partition is specified, the pop up will appear right after the quickView window is created:

https://github.com/aleksey-hoffman/sigma-file-manager/blob/47ce65bdac78e5c9b17315f16623d40d81dcf1bb/src/electronMain.js#L698

UPDATE:

Smaller reproduction example:

I was able to reproduce it with this code:

let window1 = null
let window2 = null

electron.app.on('ready', async () => {
  createWindow1()
  createWindow2()

  setTimeout(() => {
    console.log('trigger window 1 download')
    window1.webContents.downloadURL('https://stackoverflow.com')
  }, 1000)
})


function createWindow2 () {
  window1.webContents.session.once('will-download', downloadHandler1)
  window2.webContents.session.once('will-download', downloadHandler2)
}

function createWindow1 () {
  window1 = new electron.BrowserWindow()
  window1.loadURL('app://./quickViewWindow.html')
  window1.webContents.session.once('will-download', downloadHandler1)
}

function createWindow2 () {
  window2 = new electron.BrowserWindow()
  window2.loadURL('app://./quickViewWindow.html')
  window2.webContents.session.once('will-download', downloadHandler2)
}

function downloadHandler1 (event, item, webContents) {
  console.log('window will-download handler 1')
}

function downloadHandler2 (event, item, webContents) {
  console.log('window will-download handler 2')
}

When the setTimeout runs, I see the following console.log() messages:

trigger window 1 download
window will-download handler 1
window will-download handler 2

As you can see from the log, the will-download event triggers event handlers of both windows

If I specify a separate partition for each window, the problem with shared event handlers gets resolved, but I get the 2nd problem mentioned above - the link association pop up on launch

window1 = new electron.BrowserWindow({
  webPreferences: {
    partition: 'partition1',
  }
})
window2 = new electron.BrowserWindow({
  webPreferences: {
    partition: 'partition2',
  }
})

Solution

  • I figured it out. If that's a wrong way to do it, please someone let me know.

    Here's how I fixed it:

    Fix for the problem #1:

    Set a custom partition name for the window, so it uses its own webContents.session instead of sharing the default one.

    Main process:

    windows.quickViewWindow = new electron.BrowserWindow({
      ...
      webPreferences: {
        partition: 'quickView',
    
    ...
    
    windows.quickViewWindow.webContents.session.once(
      'will-download',
      (event, item, webContents) => {
        event.preventDefault()
        ...
      }
    )
    

    quickViewWindow.html:

    ipcRenderer.on('load:webview', (event, data) => {
      ...
      webviewNode.setAttribute('partition', 'quickView')
    

    Fix for the problem #2:

    Set file:// protocol in the production path for the window URL:

    productionPath = `file://${__static}/quickViewWindow.html`
    

    Here's the commit: https://github.com/aleksey-hoffman/sigma-file-manager/commit/31208809cda7614a7c2f32237ae14f6c9c602f8f