5

Summary of my Goal

I want to load a third party website into Electron and manipulate its CSS. I want the CSS changes I made to persist. I want to bundle the code as an app and the CSS changes to persist when the user runs the app. The website I want to embed uses google for authentication.

  • The First Approach

The first approach I tried was to change the CSS in the browser via the Google Chrome "Overrides" feature. This did not work as the changes do not persist to users when they open the app on their machines.

The second approach is documented in this question I posted: In Electron (Forge) when nodeIntegrationInWorker: true, embedding gmail shows a blank screen

  • The Second Approach

Note - In my code below I've used gmail.com as the example website to load. It creates the same problem.

index.js

const { app, BrowserWindow } = require('electron');
const path = require('path');




const createWindow = () => {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    // width: 1000,
    // height: 800,
    // resizable: false,
    // frame:false,
    webPreferences: {
       preload: path.join(__dirname, 'preload.js'),
       nodeIntegrationInWorker: true,
      contextIsolation: true
    },
  });

;
mainWindow.loadURL("https://gmail.com")
mainWindow.webContents.openDevTools();



};

app.on('ready', createWindow);


app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate', () => {

  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow();
  }
});

preload.js

function changeCSSonLoad(){

    let ele1  = document.querySelector('123-some large class list > div > div.flex.h-full.flex-1.flex-col.md\\:pl-\\)');
    let ele2  = document.querySelector('456-some large class list > div > div.flex.h-full.flex-1.flex-col.md\\:pl-\\)');
    let ele3  = document.querySelector('789-some large class list > div > div.flex.h-full.flex-1.flex-col.md\\:pl-\\)');

    ele2.style.display = "none";
    ele3.style.display = "none";
    ele4.style.display = "none";



    console.log("IT WERRRRKS")

}

setTimeout(function(){ changeCSSonLoad(); }, 1000);  

The code above works fine after the user is logged in. If the user is not logged in and they are prompted to do so via Google, the screen turns blank white (the connection is severed). It is exactly what happens in this Github post: https://github.com/electron/electron/issues/8950

The solution in the previous link is to remove nodeIntegrationInWorker: true from my code. If I do that the CSS changes no longer work.

I've been online reading about preload.js and from the looks of it I am not using it correctly. This post talks about the "Proper" way to use preload.js

https://stackoverflow.com/questions/57807459/how-to-use-preload-js-properly-in-electron#:~:text=The%20proper%20way%20to%20use,here%20for%20more%20explanation%20why).

In summary,

  • I want to load a third party website into Electron and manipulate its CSS. The website I want to load uses Google for authentication. I have created a version that works except when users are prompted to authenticate.
  • I am not sure if I am using preload.js the right way and by proxy I am not sure if I am going about solving this entire problem the right way in Electron.
William
  • 4,422
  • 17
  • 55
  • 108
  • 2
    Couldn't you simply include a stylesheet with the overrides instead of doing it via JavaScript? – exside Jan 16 '23 at 15:46

2 Answers2

1

There are a few ways to load an external website into an Electron app and manipulate its CSS. One way is to use the webview tag, which allows you to embed a web page within your Electron app. You can use the src attribute to specify the URL of the external website you want to load, and then use JavaScript to manipulate the webview's DOM and styles.

Another way is to use the BrowserWindow module to create a new window and load the external website into it. You can use the webContents property of the BrowserWindow to execute JavaScript on the page and change its CSS.

To persist the changes you made to the web page's CSS, you can either use the localStorage or indexedDB to save the changes in the browser.

Here is an example of how to load an external website into an Electron app, manipulate its CSS and persist the changes:


// In the main process
const { BrowserWindow } = require('electron')

// Create a new window
let win = new BrowserWindow({ width: 800, height: 600 })

// Load the external website
win.loadURL('https://example.com')

// Get the web contents of the window
let contents = win.webContents

// Inject some CSS into the page
contents.insertCSS(`
  body {
    background-color: red;
  }
`)

// Save the CSS changes to localstorage
contents.executeJavaScript(`
  localStorage.setItem('css', 'body {background-color: red;}');
`)

In the render process you can retrieve the css from localStorage and insert it into the webview again


// In the renderer process
let css = localStorage.getItem('css')

// Inject the saved CSS
let webview = document.getElementById('webview')
webview.addEventListener('dom-ready', () => {
  webview.insertCSS(css)
})

Please note that the above example is just one way to achieve this and it might need some adjustments to fit your requirements. Additionally, you might need to take care of some security issues like cross-site scripting (XSS) and cross-site request forgery (CSRF) if you want to load external website inside your electron app.

0

I hope this helps, but I experimented a bit and did the following...

Please note that the following code is in a self-invoked async function

Get the HTML for gmail using node-fetch.

const html = await fetch('https://gmail.com').then(resp => resp.text())

Create the dom in memory using JSDOM

const dom = new JSDOM(html);
const document = dom.window.document;

Augment this virtual dom however you like

const ele = document.querySelector('.BDEI9.LZgQXe');
const ele2 = document.createElement('div');
ele2.innerHTML = 'Aweh!!!!';
ele.appendChild(ele2)

Export updated HTML as base64

let buffer = Buffer.from(dom.serialize());
const base64 = "data:" + 'text/html' + ';base64,' + buffer.toString('base64');

Now load the page

mainWindow.loadURL(base64)
mainWindow.webContents.openDevTools();

The final code looks like this

const { app, BrowserWindow } = require('electron')
const fetch = async (...args) => await import('node-fetch').then(({ default: fetch }) => fetch(...args));
const path = require('path')
const jsdom = require("jsdom");
const { Buffer } = require('buffer');
const { JSDOM } = jsdom;


function createWindow() {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  });

  ;(async () => {
    // Get the html for gmail using node-fetch
    const html = await fetch('https://gmail.com').then(resp => resp.text())

    // Create the dom in memory
    const dom = new JSDOM(html);
    const document = dom.window.document;

    // Augment the dom
    const ele = document.querySelector('.BDEI9.LZgQXe');
    const ele2 = document.createElement('div');
    ele2.innerHTML = 'Aweh!!!!';
    ele.appendChild(ele2)

    // Export updated html content as base64
    let buffer = Buffer.from(dom.serialize());
    const base64 = "data:" + 'text/html' + ';base64,' + buffer.toString('base64');

    // Load page
    mainWindow.loadURL(base64)
    mainWindow.webContents.openDevTools();
  })()
}

Hopefully, this helps.

Kitanga Nday
  • 3,517
  • 2
  • 17
  • 29