How to use <webview> and Measurement Protocol with Electron
Tags: react, typescript, opensource, tutorial
Published: 2022-06-14 11:44:12

## Abstract
Bonb is a browser based on Electron. It's being developed by a small international team. It's open source, and can be downloaded on github: https://github.com/danielfebrero/bonb-browser

In this article, we will use terms such as Main or Renderer process. We assume that you have at least a basic knowledge of Electron.

Also, we are looking for beta testers and cofounders of any kind.

## Securing Electron Webview tag

### Preload a script
Instead of setting a preload attribute in the <webview> tag from the Renderer process, we force it's value directly in the Main process. It reduces the attack vectors and facilitates the import of the preload file.


```
app.on('web-contents-created', (_event, contents) => {
  contents.on('will-attach-webview', (_wawevent, webPreferences, _params) => {
    webPreferences.preloadURL = `file://${__dirname}/webview-preload.js`;
  });
}); 
```


### Opening pop-ups
A browser needs to support pop-ups and new windows. What we did is to intercept the 'new-window' event, and use a custom method 'addBrowser', after filtering the url with an adblocker.

In the renderer:

```
webview.addEventListener('new-window', (e) => {
    if (!isBlockedUrl(e.url)) addBrowser({ url: e.url });
});
```

Also, to prevent a new window opening from the Main process, we intercept the event and prevent default:

```
app.on('web-contents-created', (_event, contents) => {
  contents.on('new-window', (e) => {
    e.preventDefault();
  });
});
```

### Permission requests
We have not completed the permission requests (webcam, mic...) implementation yet. Meanwhile, we are rejecting all requests:

```
session
      .fromPartition('user-partition')
      .setPermissionRequestHandler((webContents, permission, callback) => {
        callback(false);
      });
```

## Implement Google Analytics in Electron
By default, Gtag is not compatible with electron, because of the file protocol used to serve the app. We won't learn how to implement Gtag in Electron, but how to use the Measurement Protocol of Google Analytics.

### Measurement protocol
The measurement protocol permits collecting analytic data through https requests. It requires to create an 'api_secret' for the property.

### Main or Renderer process?
Because of CORS policy, it's less secure to implement Google Analytics in the renderer. You will prefer send the request from the Main process, after receiving events from the Renderer process.

For example, in the Main process:

```
const sessionId = v4();

const makePayload = (eventName: string, params?: Record<string, unknown>) => {
  return {
    client_id: machineIdSync(),
    events: [
      {
        name: eventName,
        params: {
          app_is_packaged: app.isPackaged ? 'true' : 'false',
          engagement_time_msec: 1,
          app_name: 'Bonb',
          app_version: appPackageJson.default.version,
          session_id: sessionId,
          ...params,
        },
      },
    ],
  };
};

export const event = (eventName: string, params?: Record<string, unknown>) => {
  const payload = makePayload(eventName, params);
  axios
    .post(
      'https://google-analytics.com/mp/collect?api_secret=&measurement_id=G-',
      payload
    )
    .catch(console.log);
};


ipcMain.on('analytics', async (_event, args) => {
  event(args.eventName, args.params);
});
```

And in the Renderer process:

```
window.electron.ipcRenderer.sendMessage('analytics', {
    eventName: 'browser_navigate',
 });
```


### Logging users
As specified in the documentation, events don't trigger a user on Google Analytics. If you want to count one user when using the Measurement Protocol, you should add two parameters in the payload: 'engagement_time_msec' and 'session_id'.