Search code examples
node.jselectronwindows-10protocols

Custom protocol scheme not working in Electron


Using the latest stable Electron (29)

Sample code is taken from the official docs:

import { app, BrowserWindow, protocol, net } from 'electron';

protocol.registerSchemesAsPrivileged([
  {
    scheme: 'foo',
    privileges: {
      bypassCSP: true,
      standard: true,
      secure: true,
      supportFetchAPI: true,
    }
  }
]);

app.whenReady().then(() => {
  console.log("app rdy")
  protocol.handle('foo', (req) => {
    console.log("yo", req.url); // <-- never executed

     return new Response('bad', {
       status: 400,
       headers: { 'content-type': 'text/html' }
     })
  })
})

app.on('ready', () => {
  win = new BrowserWindow({
    width,
    height,
    x,
    y,
    frame: false,
    closable: true,
    webPreferences: {
      preload: `preload.js`,
      nodeIntegration: false,
      nodeIntegrationInSubFrames: false,
      contextIsolation: true,
    },
  });

   win.loadURL(`foo://main.html`);
});

So what is supposed to do - is to register the foo protocol, then load in the main window an url with the foo:// scheme. This should be handled by the foo handler function, but it's not. The function never runs, and instead some Windows window is opened with a box telling me I need to download some app from the windows store to open "foo" files!? wtf?

So I did some more research and found this function: app.setAsDefaultProtocolClient(protocol) which I assume is supposed to make the electron app be the default app that opens foo:// links.. So after I added app.setAsDefaultProtocolClient('foo') hell ensued and the electron app kept opening new instances of itself continuously until I had to forcibly power down my PC because it become unresponsive.

My guess is that Electron passes any non-http or file urls to Windows for some weird reason, and it doesn't take into account protocol.handle and/or registerSchemesAsPrivileged...

Anyone else facing the same issue?


Solution

  • Its to do with sessions! from: Official docs

    A protocol is registered to a specific Electron session object. If you don't specify a session, then your protocol will be applied to the default session that Electron uses. However, if you define a partition or session on your browserWindow's webPreferences, then that window will use a different session and your custom protocol will not work if you just use electron.protocol.XXX

    This works:

    import { app, BrowserWindow, protocol, session } from 'electron';
    
    const path = require('node:path')
    const url = require('url')
    
    protocol.registerSchemesAsPrivileged([
      {
        scheme: 'foo',
        privileges: {
          bypassCSP: true,
          standard: true,
          secure: true,
          supportFetchAPI: true,
        }
      }
    ]);
    
    app.whenReady().then(() => {
    
      const partition = 'persist:subdeveloper'
      const ses = session.fromPartition(partition)
    
      console.log("app rdy")
      ses.protocol.handle('foo', (req) => {
        console.log("yo", req.url); // <-- never executed
    
         return new Response('bad', {
           status: 400,
           headers: { 'content-type': 'text/html' }
         })
      })
    
      const mainWindow = new BrowserWindow({ webPreferences: { partition } });
    
      mainWindow.loadURL(`foo://main.html`);
    })