capacitor-community / electron

Deploy your Capacitor apps to Linux, Mac, and Windows desktops, with the Electron platform! 🖥️
https://capacitor-community.github.io/electron/
MIT License
335 stars 59 forks source link

How do electron plugins run code on the render process? #154

Closed RandomEngy closed 2 years ago

RandomEngy commented 2 years ago

I've set up my Capacitor project and have an electron folder. I've got the basics working with bundle.js created by webpack referenced by index.html, and both of those are in the electron/app folder.

The boilerplate code serves up this app folder, which has only the pure code from the root web app.

The plugins appear to be running in the main process. How would you run custom electron code in the renderer process? Normally you'd do some ipc and send a message to the render process and tell it to do something. But the page itself is just a copy of the generic web project, so you can't be pulling in electron libraries to listen to those messages.

So far I've gotten this to work:

BrowserWindow.getFocusedWindow().webContents.executeJavaScript("console.log('testing');");

But that method of running JS code seems limiting. I'm a bit new to both electron and capacitor so I could be missing something obvious here.

IT-MikeS commented 2 years ago

Have a look at the Context Bridge docs here: https://www.electronjs.org/docs/latest/api/context-bridge

Use that with the preload script

RandomEngy commented 2 years ago

That looks promising, thanks!

RandomEngy commented 2 years ago

Jumping back into this today.

I figured out the plugin is running in the Main process as it complains about "window" not being defined. I'm having it find the focused window and send a message to it:

export class MyElectronPlugin implements MyLocalPlugin {
    async echo(options: { value: string }): Promise<{ value: string }> {
        BrowserWindow.getFocusedWindow().webContents.send("echo", options.value);
        return options;
    }
}

I had to add electron to the plugin dev dependencies since the example plugin package.json it doesn't import electron and is only using Node APIs.

Then in the preload script, listen for it:

const { contextBridge, ipcRenderer } = require('electron');

ipcRenderer.on("echo", (event, message) => {
    console.info(message);
});

So it ends up hopping over the process boundary a couple times to get back to where it started.

RandomEngy commented 2 years ago

For posterity, I figured things out a bit more. The right answer in this case is to skip the plugin entirely. Plugins run in the main process and you need to use IPC to get back to the render process. The plugins are a bit better for things that can run in the main process (like geolocation or file system access) and not for things that need to run inside a browser context and fiddle with electron windows (like AWS cognito auth).

The part that I was missing was that there are also two separate contexts inside the render process. preload.ts runs in one context while the main website runs in a different context. They don't share the same window object so you can't just set up functions in the global scope for the page to call. contextBridge is what you need to use to expose preload.ts code to the main website. So all the electron-specific code can go in preload.ts and a capacitor plugin is not involved.

I haven't quite figured out what I will do for the other platforms but I may be able to also inject some special functions with the same interface into the page.