Electron.js | Remove new window frame - menu

I'm using an index.html including an iframe. When a page is opened in a new tab with CTRL + Click keys, the properties I defined for BrowserWindow are not applied.
I want to remove the frame of the opened new tab. How can I do it?
const path = require('path')
const {app, BrowserWindow, Menu} = require('electron')
function createWindow() {
const win = new BrowserWindow({
width: 1000,
height: 700,
frame: false,
show: false,
})
win.loadFile('index.html')
win.once('ready-to-show', () => { win.show() });
}
const menu = Menu.buildFromTemplate([])
Menu.setApplicationMenu(menu)
app.whenReady().then(() => { createWindow() })

Windows opened via links don't inherit their parents options, instead, you have to register a window open handler and set the options manually:
win.webContents.setWindowOpenHandler(({ url }) => {
return {
action: 'allow',
overrideBrowserWindowOptions: { // These options will be applied to the new BrowserWindow
frame: false,
// other BrowserWindow settings
}
}
}
On the documentation: https://www.electronjs.org/docs/latest/api/window-open#native-window-example

Related

Electron | TypeError: "x" is not a constructor

I have a simple electron app that opens a browser window to a url of a webpage.
I import my stuff using
const { BrowserWindow, app, MessageChannelMain } = require('electron');
I create my window on app.on("ready") using window = new BrowserWindow({ ... }); and everything works fine.
When I try to create a MessageChannelMain using const {port1, port2} = new MessageChannelMain();, I get the error:
TypeError: MessageChannelMain is not a constructor.
Full code:
function init_window(){
if ( mainWindow == null ) {
mainWindow = create_window();
load_window(); // calls mainWindow.loadURL(myURL);
const {port1, port2} = new MessageChannelMain();
port2.postMessage({ test: 21 })
port2.on('message', (event) => {
console.log('from renderer main world:', event.data)
});
port2.start();
mainWindow.webContents.postMessage('main-world-port', null, [port1])
mainWindow.on('closed', function(event) {
mainWindow = null;
});
mainWindow.webContents.on("did-fail-load", function(event) {
})
}
}
function create_window() {
return new BrowserWindow({
width: 1200,
height: 800,
darkTheme: true,
webPreferences: {
contextIsolation: true,
preload: path.join(__dirname, 'preload.js'),
},
});
}
When I console.log(MessageChannelMain); I get undefined.
At the same time, I get other errors when I try to use some electron stuff on the preload script:
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld(
'electron',
{
doThing: () => ipcRenderer.send('do-a-thing')
}
)
Here I am getting TypeError: Cannot read property 'exposeInMainWorld' of undefined

Kaspersky marks my Electron app as a Trojan

I recently made an Electron app that is essentially a web browser and it has 3 windows and they all get destroyed when the user closes them, leaving no excess process behind this is the only complex part of the app but Kaspersky flags it as a Trojan.
my index.js
const { app, BrowserWindow, ipcMain, desktopCapturer } = require('electron');
const path = require('path');
app.commandLine.appendSwitch("disable-http-cache");
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
// eslint-disable-next-line global-require
if (require('electron-squirrel-startup')) {
app.quit();
}
function createMacroWindow(macroUrl) {
let macroWindow = new BrowserWindow({
width:800,
height:500,
frame:false,
show:false,
icon: path.join(__dirname, "./assets/icons/icon.png"),
webPreferences:{
devTools: false,
webviewTag: true,
nodeIntegration: true,
contextIsolation: false
}
})
macroWindow.loadFile(path.join(__dirname, './windows/macro-window.html'));
macroWindow.webContents.on('did-finish-load', () => {
macroWindow.webContents.send("loadURL", macroUrl)
})
macroWindow.center();
macroWindow.openDevTools();
macroWindow.show();
macroWindow.on("close", () => {
macroWindow = null;
})
require("#electron/remote/main").enable(macroWindow.webContents);
}
const createWindow = () => {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 1000,
height: 700,
frame:false,
show:false,
icon: path.join(__dirname, "./assets/icons/icon.png"),
webPreferences: {
webviewTag: true,
nodeIntegration: true,
devTools: true, // Geliştirici konsolunu kökten kısıtlayan kod (bu olmazsa CTRL+SHIFT+I yapınca yine açılır)
contextIsolation: false,
},
});
let splashScreen = new BrowserWindow({
width:1000,
height:700,
frame:false,
icon: path.join(__dirname, "./assets/icons/icon.png"),
webPreferences: {
devTools:false
}
})
// and load the index.html of the app.
mainWindow.loadFile(path.join(__dirname, 'index.html'));
splashScreen.loadFile(path.join(__dirname, './windows/splash-window.html'));
splashScreen.center();
function destroySplashScreen() {
splashScreen.close();
splashScreen = null;
}
mainWindow.webContents.on('did-finish-load', function() {
mainWindow.show();
mainWindow.maximize();
splashScreen.isDestroyed() ? console.log("splash screen already destroyed") : destroySplashScreen();
});
require('#electron/remote/main').initialize()
require("#electron/remote/main").enable(mainWindow.webContents)
ipcMain.handle(
'DESKTOP_CAPTURER_GET_SOURCES',
(event, opts) => desktopCapturer.getSources(opts)
)
let macroUrl;
ipcMain.on("openMacroWindow", (e, urlToLoad) => {
macroUrl = urlToLoad;
createMacroWindow(macroUrl);
})
ipcMain.on("closeApp", () => {
BrowserWindow.getAllWindows().forEach(window => window.close());
})
ipcMain.on('clearCache', () => {
mainWindow.webContents.session.clearStorageData([], function (data) {
console.log(data);
})
mainWindow.webContents.session.clearCache();
})
mainWindow.on("close", () => {
app.quit();
})
// Open the DevTools.
// mainWindow.webContents.openDevTools(); // Geliştirici konsolunu kapatmak için bu satırı silebilirsiniz
};
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);
// app.disableHardwareAcceleration()
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and import them here.
splashScreen is the window that opens while the mainWindow loads, it closes when the main loads and is set to null afterwards just like the macroWindow.
macroWindow is opened when the ipcMain is told to openMacroWindow
Fix
I downgraded to electron-forge version 5.2.4 from 6.x version, this fixed my issue but this is not an ideal solution.

How to create a context menu in a webview in electron

I am working on a small electron app, and I seem to have hit a road block. I have a webview in my app that loads to www.google.com . The webview loads fine, but when I right click anywhere in the webview, no context menu is created, clicking on any part of the page that is not the webview results in a context menu being successfully created. I am using electron-content-menu to create the menus, and I read through the documentation included at the git repository: https://github.com/sindresorhus/electron-context-menu#readme, but got nothing of value. Any help would be appreciated, as I really need context menu's to appear in my webviews.
Main.js
// Modules to control application life and create native browser window
const {app, BrowserWindow, Menu, shell} = require('electron')
const path = require('path')
var holder;
var checkmax = 0;
function createWindow () {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 1919,
height: 1079,
frame: true,
webPreferences: {
webviewTag: true,
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html');
holder = mainWindow;
// Open the DevTools.
// mainWindow.webContents.openDevTools()
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
createWindow()
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
//testing context
const contextMenu = require('electron-context-menu');
contextMenu({
prepend: (defaultActions, params, browserWindow) => [
{
label: 'Rainbow',
// Only show it when right-clicking images
visible: params.mediaType === 'image'
},
{
label: 'Search Google for “{selection}”',
// Only show it when right-clicking text
visible: params.selectionText.trim().length > 0,
click: () => {
shell.openExternal(`https://google.com/search?q=${encodeURIComponent(params.selectionText)}`);
}
}
]
});
Index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body style="background-color:red;">
<webview style="position: absolute; top: 10vh; left:0; width:100vw; height: 90vh;" src="https://www.google.com/"></webview>
</body>
</html>
I figured it out after a ton of trial and error, and I came up with this, which works for any generated webviews within the page after the initialization.
This goes in Main.js
var menu = new Menu();
//Basic Menu For Testing
menu.append(new MenuItem({ label: 'MenuItem1', click: function() { console.log("YES");
} }));
menu.append(new MenuItem({ type: 'separator' }));
menu.append(new MenuItem({ label: 'MenuItem2', type: 'checkbox', checked: true }));
app.on("web-contents-created", (...[/* event */, webContents]) => {
//Webview is being shown here as a window type
console.log(webContents.getType())
webContents.on("context-menu", (event, click) => {
event.preventDefault();
console.log(webContents.getType())
menu.popup(webContents);
}, false);
});

Redirect electron popup to BrowserView

When a popup is created I'd like to display it in a BrowserView.
Is there a way of piping the entire popup request into a BrowserView?
The example below works for simple urls but not for more complex popups (eg window.open creates an about:blank page and uses document.write to generate content). Can be tested with "Method #1.1" on https://webbrowsertools.com/popup-blocker/.
const { app, BrowserWindow, BrowserView } = require('electron')
function createWindow() {
const mainWindow = new BrowserWindow({
width: 800,
height: 600
})
mainWindow.webContents.on('new-window', (event, url, frameName, disposition, options, additionalFeatures) => {
event.preventDefault();
const view = new BrowserView()
mainWindow.setBrowserView(view)
view.setBounds({ x: 0, y: 0, width: 800, height: 800 })
view.webContents.loadURL(url)
})
mainWindow.loadURL("https://webbrowsertools.com/popup-blocker/")
}
app.whenReady().then(() => {
createWindow()
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})

Electron loading animation

Could someone please help me to implement a loading animation for my Electron app ?
I am learning this new technology and it would be nice to understand the correct way to do that.
I am thinking about something like :
app.on('ready', () => {
// show main content
})
app.on('not-ready', () => {
// show loading animation
})
As far as I know there is no event emitted from app before ready (only exception is will-finish-launching available only on macOS).
Furthermore, you cannot open any BrowserWindow before app is ready, so you should really wait it.
However, if your main application window loading very slow, you can still open a "loading window" before that and switch them when your main window is ready.
const { app, BrowserWindow } = require('electron')
app.on('ready', () => {
let main = null
let loading = new BrowserWindow({show: false, frame: false})
loading.once('show', () => {
main = new BrowserWindow({show: false})
main.webContents.once('dom-ready', () => {
console.log('main loaded')
main.show()
loading.hide()
loading.close()
})
// long loading html
main.loadURL('http://spacecrafts3d.org')
})
loading.loadURL('loding.html')
loading.show()
})
You can use win.on('ready-to-show') instead of win.webContents.on('dom-ready') everywhere if you want to eliminate visual flash (but losing some speed)
window.open()
If you want to do the same for BrowserWindow opened in renderer process by window.open(), you can use new-window event of webContents if nativeWindowOpen is true
main = new BrowserWindow({
webPreferences: {
nativeWindowOpen: true
}
})
main.webContents.on('new-window', (event, url) => {
// there are more args not used here
event.preventDefault()
const win = new BrowserWindow({show: false})
win.webContents.once('dom-ready', () => {
win.show()
loading.hide() // don't destroy in this case
})
win.loadURL(url)
loading.show()
event.newGuest = win
})
It can be done by displaying a new BrowserWindow displaying the activity loader , until the main app fully loads .
Let's define a createWindow funtion (as given in docs) which is responsible for loading the main app for the user as :
var loadingwindow = null; // Responsible for creating activity loader
function createWindow () {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
win.loadFile('index.html')
loadingwindow.hide() // Used to hide the loading screen when the contents in main app are loaded
}
Now , in order to display the loadingwindow screen , we need to place it in app.on('ready' , callback_fn) as shows here :
app.on("ready" , () => {
loadingwindow = new BrowserWindow({
frame : false,
movable : false,
})
loadingwindow.loadFile('activity.html') // To load the activity loader html file
loadingwindow.show();
})
That's it ! ,
To check if it is working properly , wrap the setTimeout function over the app.whenReady().then(createWindow)

Resources