Messages between `BrowserView` and the `renderer` React page in `Electron
Asked Answered
C

1

9

I'm trying to figure out how to exchange messages between the main process and the BrowserView.

I've been using the ipc between the main process and the renderer process within a "simple" react renderer page.

But now, using the same technique, I do not see the received message in the console of the BrowserView, which, as far as I understand, seems to behave differently from a "normal" react renderer page. And this SOF post seems confirm my hypothesis: Electron BrowserView Renderer process vs WebView

In preload I defined:

declare global {
    interface Window {
        api: {
            electronIpcSend: (channel: string, ...arg: any) => void;
            electronIpcOn: (channel: string, listener: (event: any, ...arg: any) => void) => void;
        }
    }
}

In main I have :

function createWindowTypeC (url_string: string) {
    WindowTypeC = new BrowserWindow({
    
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            nodeIntegrationInWorker: false,
            sandbox: true,
            nodeIntegrationInSubFrames: false,
            webSecurity: true,
            webviewTag: false,
            preload: path.join(__dirname, "./preload/preload.js"), /* eng-disable PRELOAD_JS_CHECK */
        }
    })
    
    // and load the index.html of the app.
    
    // Emitted when the window is closed.
    WindowTypeC.on('closed', () => {
        WindowTypeC.removeBrowserView(view_C)
        WindowTypeC = null
    })
    
    enforceInheritance(WindowTypeC.webContents)
    
    // https://www.electronjs.org/docs/latest/api/browser-view
    
    let view_C = new BrowserView()
    
    
    view_C.webContents.once('did-finish-load', () => {
        contents.focus()

        view_C.webContents.send('ping', 'whoooooooooh!')
    })
    
    view_C.setAutoResize({
        width: true,
        height: true,
        horizontal: true,
        vertical: true
    })
    
    WindowTypeC.setBrowserView(view_C)
    view_C.setBounds({ x: 0, y: 0, width: 800, height: 600 })
    view_C.setBackgroundColor("#1e1e1e")
    
    setTimeout(() => {
        view_C.webContents.loadURL(url_string)
    }, 200)
    
    view_C.webContents.openDevTools({
        mode: 'left'
    })
}

While in the App_C.tsx I have:

import * as React from 'react';
    
function App_C(props) {
    
    window.api.electronIpcOn('ping', (event, message) => {
        console.log("App_C-window.api.electronIpcOn-ping-message: ", message)
     })

     window.api.electronIpcSend('pong', "Hello from App_C.tsx")

     return (
         <div id="outer-container" className='outer-container'>
             <h1>App_C</h1> 
         </div>
     );
}
    
export default App_C;

Executing the code, the page gets correctly rendered, but I do not see any message sent from the BrowserView to the renderer process.

Second, collateral, question, is: how to send a message on the way back, in the BrowserView sent by the renderer process?

And third question: how to interact with the page rendered by the BrowserView?

enter image description here

Crepuscular answered 23/2, 2022 at 20:45 Comment(2)
Were you able to solve this in the end? I am facing the same problem.Incitement
Even I have a requirement of communicating between BrowserView and Main/Renderer. After going to tons of resources I did not find it. Were you able to solve it?Cattegat
L
0

To send a message from a browserview to the main process, you can use console.log from the browserview and monitor console-message in the main process:

view.webContents.on("console-message", (evt, logLevel, message, lineNumber, source) => {
  if (source.endsWith("/mybrowserview.js"))
    // do something with message
});

Note that this will pick up all console messages, so you will need to check that the message is in the format expected and ignore it if not. For example, your message could be in the format:

console.log('{"request":"myrequest","value":"myvalue"}');

and you can then check that it is a request with something like:

view.webContents.on("console-message", (evt, logLevel, message, lineNumber, source) => {
  if (source.endsWith("/mybrowserview.js"))
      if (!isJsonString(message) || !message.includes('"request"'))
          return;

      // Looks like a valid request - do something with it
      let jsn = JSON.parse(message);

      if(jsn.request == "myrequest")
          ...
});


function isJsonString(str) {
  try {
    JSON.parse(str);
  } catch (e) {
    return false;
  }
  return true;
}

To send a message from main to browserview, you can execute some javascript, like call a function that is already in the browserview:

view.webContents.executeJavaScript('myfunction("some message")');

And in the browserview you would have something like:

function myfunction(message) {
  // do something with message
  if (message == "whatever")
      ...
}
Loyalist answered 5/12, 2023 at 10:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.