Dynamically Execute JavaScript in ElectronJS

ElectronJS is an Open Source Framework used for building Cross-Platform native desktop applications using web technologies such as HTML, CSS, and JavaScript which are capable of running on Windows, macOS, and Linux operating systems. It combines the Chromium engine and NodeJS into a Single Runtime.

In Electron, every BrowserWindow Instance can be thought of as an individual webpage within the application. Electron creates and controls these BrowserWindow Instances using the BrowserWindow Object and the webContents property. In traditional web applications, we can type in JavaScript code within the console of the browser for it be executed on the webpage. For achieving the same via scripts, we need to use a browser plugin or an extension. In Electron, the webContents property provides us with certain Instance methods by which we can dynamically inject JavaScript code within the BrowserWindow Instance during runtime. This tutorial will demonstrate how to use those Instance methods of the webContents property.

We assume that you are familiar with the prerequisites as covered in the above-mentioned link. For Electron to work, node and npm need to be pre-installed in the system.

  • Project Structure:
    Project Structure

Example: Follow the Steps given in Build a Desktop Application using ElectronJS to setup the basic Electron Application. Copy the Boilerplate code for the main.js file and the index.html file as provided in the article. Also perform the necessary changes mentioned for the package.json file to launch the Electron Application. We will continue building our application using the same code base.
package.json:

{
  "name": "electron-execute",
  "version": "1.0.0",
  "description": "Inject JS Code in Page ",
  "main": "main.js",
  "scripts": {
    "start": "electron ."
  },
  "keywords": [
    "electron"
  ],
  "author": "Radhesh Khanna",
  "license": "ISC",
  "dependencies": {
    "electron": "^8.3.0"
  }
}

Create the assets folder according to the project structure. Create the sample.txt file in the assets folder for demo purposes.
sample.txt:
sample.txt
Output: At this point, our basic Electron Application is set up. Upon launching the application, we should see the following Output:
GUI Output

Dynamically Inject JS in Electron: The BrowserWindow Instance and webContents Property are part of the Main Process. To import and use BrowserWindow in the Renderer Process, we will be using Electron remote module.



index.html: Add the following snippet in that file.

filter_none

edit
close

play_arrow

link
brightness_4
code

<h3>Dynamically Inject JS</h3>
  <button id="inject">Read sample.txt File</button>
  <button id="print">Print an Array</button>

chevron_right


The Read sample.txt File and Print an Array buttons do not have any functionality associated with them yet. To change this, add the following code in the index.js file.

index.js:

filter_none

edit
close

play_arrow

link
brightness_4
code

const electron = require('electron')
  
// Importing BrowserWindow from Main Process using Electron remote
const BrowserWindow = electron.remote.BrowserWindow;
  
var inject = document.getElementById('inject');
let win = BrowserWindow.getFocusedWindow();
// let win = BrowserWindow.getAllWindows()[0];
  
inject.addEventListener('click', (event) => {
    win.webContents.executeJavaScript('const path = require("path");'
        + 'const fs = require("fs");'
        + 'fs.readFile(path.join(__dirname, "../assets/sample.txt"), '
        + '{encoding: "utf-8"}, function(err, data) {'
        + 'if (!err) { console.log("received data: " + data); }'
        + 'else { console.log(err); } });', true)
        .then(console.log('JavaScript Executed Successfully'));
});
  
var print = document.getElementById('print');
print.addEventListener('click', (event) => {
  
    var webSource = {
        code: 'var numbers = [1, 2, 3, 4, 5];' 
        + 'function filters(num) { return num > 2; }' 
        + 'console.log(numbers.filter(filters));',
        url: '',
        startLine: 1
    }
  
    win.webContents.executeJavaScriptInIsolatedWorld(1, [webSource], true)
        .then(console.log('JavaScript Executed Successfully'));
});

chevron_right


Explanation: The webContents.executeJavaScript(code, userGesture) method simply executes the code in the webpage i.e. the BrowserWindow Instance. This method returns a Promise and it is resolved with the result of the executed code or the Promise is rejected if the result of the code itself is a rejected Promise. This means that the Promise can return any datatype including an object based on the result of the executed code. In case, the executed code throws an Error, it will be displayed on the console. In case, the executed code does not return a Promise but implements a callback instead, then this Promise will be resolved to a void as demonstrated in the above code. It takes in the following parameters. The code execution will be suspended until the webpage is loaded completely. In our code, this method is Invoked by clicking on the Read sample.txt File button.

  • code: String This value cannot be empty. This value represents the code to be executed in the BrowserWindow instance. In the above code, we have read the contents of the sample.txt file using the fs.readFile() method. The fs.readFile() implements a callback and returns the contents of the file.
  • userGesture: Boolean (Optional) In the BrowserWindow Instance, some HTML APIs like requestFullScreen can only be invoked by a gesture from the user. This limitation can be removed by setting the userGesture property to true. Default value is false.

The webContents.executeJavaScriptInIsolatedWorld(worldId, scripts, userGesture) also executes the code in the webpage but it does so in an Isolated Context. This method also returns a Promise and it behaves in the same way as described for the webContents.executeJavaScript() method. It takes in the following parameters. In our code, this method is Invoked by clicking on the Print an Array button.

  • worldId: Integer This value represents the ID of the virtual isolated world to run the JavaScript code in. The default worldID is 0. Electron’s contextIsolation feature uses 999 as the worldID. We can provide any Integer value here. The JavaScript code executed in this virtual isolated world cannot interact with the code of the BrowserWindow Instance and it is evaluated as a completely separate code. Using this method, we can create any number of virtual isolated worlds to run different segmets of the JavaScript code.
  • scripts: webSource[] This property takes in an Array of webSource Objects. Hence this method can execute multiple different code blocks within the same world. Each webSource object takes in the following parameters.
    • code: String This value cannot be empty. This value represents the code to be executed in the BrowserWindow instance. In the above code, we have used the array.filter() function to create a new array satisfying the condition of any number which is greater than 2 from a sample array of numbers.
    • startLine: Integer (Optional) Default value is 1. As the name suggets, this value represents the Integer from which the code String should be evaluated.
  • userGesture: Boolean (Optional) In the BrowserWindow Instance, some HTML APIs like requestFullScreen can only be invoked by a gesture from the user. This limitation can be removed by setting the userGesture property to true. Default value is false.

Note: The webContents.executeJavaScript() method can interact with code of the BrowserWindow Instance and hence we can also use NodeJS functions in the code. For example, we can use the require function to import the fs and path modules and they will be recognized by the code. The webContents.executeJavaScriptInIsolatedWorld() method cannot interact with the code of the BrowserWindow Instance and hence we cannot use NodeJS functions since it will not recognize them. In the webContents.executeJavaScriptInIsolatedWorld() method we can only execute pure client-side JavaScript code. In case NodeJS functions are used, it will display an Error in console.

To get the current BrowserWindow Instance in the Renderer Process, we can use some of the Static Methods provided by the BrowserWindow object.

  • BrowserWindow.getAllWindows(): This method returns an Array of active/opened BrowserWindow Instances. In this application, we have only one active BrowserWindow Instance and it can be directly refered from the Array as shown in the code.
  • BrowserWindow.getFocusedWindow(): This method returns the BrowserWindow Instance which is focused in the Application. If no current BrowserWindow Instance is found, it returns null. In this application, we only have one active BrowserWindow Instance and it can be directly referred using this method as shown in the code.

Output:

full-stack-img




My Personal Notes arrow_drop_up

Check out this Author's contributed articles.

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.


Article Tags :

Be the First to upvote.


Please write to us at contribute@geeksforgeeks.org to report any issue with the above content.