Electron, I can't use BrowserWindow to explore file system - electron

I'm trying to show a folder of the machine running an Electron app, I already look and see I can load the url file:///d:/ on my machine and see the folder content but noway to do that with a BrowserWindow. I can see in the DevTools an error saying
Not allowed to load local resource.
Is there a workaround or any setup to be able to do that?
I use the following code:
ipcMain.on('openExplorer', (event, arg) => {
exploreWindow = new BrowserWindow({ width: 120, height: 82, title: "MyApp", icon: "assets/images/favicon.ico" });
exploreWindow.setTitle('Files Explorer');
addr = 'file:///d://' ;
console.log(addr);
exploreWindow.loadURL(addr);
exploreWindow.openDevTools();
});

For security reasons it is a good idea to not grant a BrowserWindow access to the filesystem. If you still want to do so you could use the inter process communication module to access the filesystem from your mainfile and send it to your BrowserWindow.
See:
Electron Documentation for ipcMain
Electron Documentation for ipcRenderer

Related

Use Mapbox with offline maps in Electron app

Is it possible to have an electron app showing offline map?
I downloaded the mbtiles and stored in the electron app, but I'm not able to show it in the angular side of the electron app.
I have the following code:
<mgl-map
class="map"
id = "map"
[style]="'mbtiles://assets/resources/downloaded.mbtiles'"
[center]="[mapCenter.longitude, mapCenter.latitude]"
(load) = "onLoad($event)"
(dragEnd)="mapDrag()"
[doubleClickZoom]="false"
[bearing]="[bearing]"
[zoom]="[zoom]"
[pitch]="[pitch]">
</mgl-map>
But I get the error
zone-evergreen.js:1068 Fetch API cannot load mbtiles://User/hello/path/to/file.mbtiles. URL scheme "mbtiles" is not supported.
So, in order to make it works in an online way I have to change the style for
[style]="'mapbox://styles/mapbox/streets-v9'"
Is it possible to make it works serving the mbtiles from the nodejs code or in other way?
To serve up your local mbtiles file, you need to register the mbtiles protocol in main.js. Something like this should do it:
const electron = require('electron');
const protocol = electron.protocol;
const path = require('path');
protocol.registerHttpProtocol('mbtiles', (req, cb) => {
const url = 'assets/resources/downloaded.mbtiles';
cb({ path: path.normalize(`${__dirname}/${url}`) })
});
You can read up on protocol handlers in electron here: https://www.electronjs.org/docs/api/protocol#protocol

"Not allowed to load local resource" with file image URL in Electron app

I am working on a note taking Electron app that uses Markdown. Currently, I'm working on inserting images into notes (using the Markdown syntax).
When inserting an image, my main process copies the image into the notes directory, then returns a file:/// URL to the image file. However, when I try to render the image, it doesn't load - and I get the error Not allowed to load local resource: file:///Users/joe/notes/images/foo.jpg.
Is there a way I can configure Electron to allow these local image URLs?
Option 1
Turning the web security off
mainWindow = new BrowserWindow({
height: 563,
useContentSize: true,
width: 1000,
webPreferences: {
webSecurity: false
}
});
Option 2
You can create your own protocol like this answer
Also here is the user that answered that question
You need register a file protocol to remove the file:/// prefix.
import { protocol } from "electron";
app.whenReady().then(() => {
protocol.registerFileProtocol('file', (request, callback) => {
const pathname = decodeURI(request.url.replace('file:///', ''));
callback(pathname);
});
});
https://github.com/electron/electron/issues/23757#issuecomment-640146333

End to end test of electron app on Windows

What I am trying to achieve:
I'd like to set up an electron project with a proper headless end-to-end testing configuration.
Issues encountered
Spectronjs seems to be the solution to achieve so. However there is no configuration to prevent the window from opening on each test. Reading some of the threads on the repository + the documentation of electron in regards to testing mentions Xvfb. I've tried to get my head around this but understand so far that this cannot be installed on Windows? And that there is no alternative.
The list on the page includes other options such as Appvoyer or CicleCI but these again are new and I am barely able to find a guide around these not to mention, I am not really liking that I have to do all these steps (link to github/bitbucket account, etc).
I've also tried to go through demo applications from the electronjs list page, but not all of them do have tests, and when they do, they are sometime written in what seems to be a different programming language, or specifically aimed for angular or react, while on my end I am aiming to use vuejs.
Can anyone point me to a clean example of an offline end to end headless test of an electron app on Windows?
There are several options how to E2E test an Electron app, unfortunately none of them is truly headless. On Windows you do not need Xvfb, it is a Linux thing. On Windows there is a "screen" available even in CI environments (I have experience with Appveyor and Azure Pipelines).
Puppeteer-core (Puppeteer-core does not contain chromium)
Spectron
Selenium-webdriver
Webdriver.io
In the past I used Spectron, but I recently switched to Puppeteer and I am very happy with the switch.
Short Puppeteer try out test file:
const electron = require("electron");
const puppeteer = require("puppeteer-core");
const delay = ms =>
new Promise(resolve => {
setTimeout(() => {
resolve();
}, ms);
});
(async () => {
try {
const app = await puppeteer.launch({
executablePath: electron,
args: ["."],
headless: false,
});
const pages = await app.pages();
const [page] = pages;
await page.setViewport({ width: 1200, height: 700 });
await delay(5000);
const image = await page.screenshot();
console.log(image);
await page.close();
await delay(2000);
await app.close();
} catch (error) {
console.error(error);
}
})();
I am testing and building an electron app in Azure Pipelines (free for open-source projects) on Win, Linux and MacOS with this config:
azure-pipelines.yml

Accessing webContents from within preload script in BrowserWindow

I want to create a "Worker" BrowserWindow from within a render process. The worker process will do some long running calculations and periodically report back.
Instead of creating the worker BrowserWindow from within the main process directly, it looks like I can use the remote module to do so from within the renderer process:
import remote from 'electron'
const workerWindow = new remote.BrowserWindow({
show: false,
webPreferences: {
preload: path.join(__dirname, 'preloadWorker.js')
}
});
workerWindow.loadURL('file://' + __dirname + '/worker.html');
I figured I can use the webContents object of the workerWindow to listen for updates like this:
workerWindow.webContents.on('worker-result', (event, msg) => console.log(msg));
But what I can't figure out how to do is emit events from the 'preloadWorker.js' file. I want to do something this in the preloadWorker.js,
setInterval(() =>{
this.webContents.send('worker-result', 'hello from worker');
}, 1000);
Now I know I could do all of this by sending messages using ipcMain and ipcRemote, but I'm trying to save myself some work...it seems like I should be able to access the webContents from within the preload.js script somehow.
This is a really old question, and you might not need it anymore. However, you can use ipcRenderer.sendToHost to communicate with the webview that your preload script is bound to.
const { ipcRenderer } = require('electron');
setInterval(() => {
ipcRenderer.sendToHost('worker-result', 'hello from worker');
}, 1000);

Passing Data to Windows in Electron

I'm learning Electron and working with multiple windows and IPC. In my main script I have the following:
var storeWindow = new BrowserWindow({
width: 400,
height: 400,
show: false
});
ipc.on('show-store-edit', function(event, store) {
console.log(store);
storeWindow.loadURL('file://' + __dirname + '/app/store.html');
storeWindow.show();
});
And in my primary window's script, I have the following inside of a click event handler, pulling in a list of stores:
$.getJSON("http://localhost:8080/stores/" + item.id).done(function(store) {
ipc.send('show-store-edit', store);
});
On the console, I am printing the store data from my server. What I'm unclear on is how to get that data into the view for my storeWindow:store.html. I'm not even sure I'm handling the sequence of events correctly but they would be:
click Edit Store
get store data from server
open new window to display store data
or
click Edit Store
open new window to display store data
get store data from server
In the latter, I'm not sure how I would get the ID required to fetch the store from the storeWindow's script.
To send events to particular window you can use webContents.send(EVENT_NAME, ARGS) (see docs). webContents is a property of a window instance:
// main process
storeWindow.webContents.send('store-data', store);
To listen for this event being sent, you need a listener in a window process (renderer):
// renderer process
var ipcRenderer = require('electron').ipcRenderer;
ipcRenderer.on('store-data', function (event,store) {
console.log(store);
});
You need the ipcMain module to achieve this... As stated in the API "When used in the main process, it handles asynchronous and synchronous messages sent from a renderer process (web page). Messages sent from a renderer will be emitted to this module."
API Docs for the ipcMain module:
https://electronjs.org/docs/api/ipc-main
To use the ipcMain you need to have nodeIntegration enabled on webPreferences
win = new BrowserWindow({
webPreferences: {
nodeIntegration: true,
}
})
Be careful this may cause security issues.
For example: Let's say we want to pass a configuration (json) file to the web page
(Triple dots (...) represent your code that is already placed inside the file, but is not relevant to this example)
main.js
...
const { readFileSync } = require('fs') // used to read files
const { ipcMain } = require('electron') // used to communicate asynchronously from the main process to renderer processes.
...
// function to read from a json file
function readConfig () {
const data = readFileSync('./package.json', 'utf8')
return data
}
...
// this is the event listener that will respond when we will request it in the web page
ipcMain.on('synchronous-message', (event, arg) => {
console.log(arg)
event.returnValue = readConfig()
})
...
index.html
...
<script>
<!-- import the module -->
const { ipcRenderer } = require('electron')
<!-- here we request our message and the event listener we added before, will respond and because it's JSON file we need to parse it -->
var config = JSON.parse(ipcRenderer.sendSync('synchronous-message', ''))
<!-- process our data however we want, in this example we print it on the browser console -->
console.log(config)
<!-- since we read our package.json file we can echo our electron app name -->
console.log(config.name)
</script>
To see the console of the browser you need to open the dev tools, either from the default Electron menu or from your code.
e.g. inside the createWindow() function
win.webContents.openDevTools()

Resources