Skip to content

Commit 42ea62b

Browse files
committed
refactor: context isolation via preload script
Signed-off-by: Adam Setch <[email protected]>
1 parent 863fcac commit 42ea62b

13 files changed

+191
-42
lines changed

config/webpack.config.main.base.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import path from 'node:path';
22
import type webpack from 'webpack';
33
import { merge } from 'webpack-merge';
4+
45
import baseConfig from './webpack.config.common';
56
import webpackPaths from './webpack.paths';
67

config/webpack.config.main.prod.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import TerserPlugin from 'terser-webpack-plugin';
22
import type webpack from 'webpack';
33
import { merge } from 'webpack-merge';
4+
45
import baseConfig from './webpack.config.main.base';
56

67
const configuration: webpack.Configuration = {

config/webpack.config.preload.base.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import path from 'node:path';
2+
import type webpack from 'webpack';
3+
import { merge } from 'webpack-merge';
4+
5+
import baseConfig from './webpack.config.common';
6+
import webpackPaths from './webpack.paths';
7+
8+
const configuration: webpack.Configuration = {
9+
devtool: 'inline-source-map',
10+
11+
mode: 'development',
12+
13+
target: 'electron-main',
14+
15+
entry: [path.join(webpackPaths.srcMainPath, 'preload.ts')],
16+
17+
output: {
18+
path: webpackPaths.buildPath,
19+
filename: 'preload.js',
20+
library: {
21+
type: 'umd',
22+
},
23+
},
24+
};
25+
26+
export default merge(baseConfig, configuration);

config/webpack.config.preload.prod.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import TerserPlugin from 'terser-webpack-plugin';
2+
import type webpack from 'webpack';
3+
import { merge } from 'webpack-merge';
4+
5+
import baseConfig from './webpack.config.preload.base';
6+
7+
const configuration: webpack.Configuration = {
8+
devtool: 'source-map',
9+
10+
mode: 'production',
11+
12+
optimization: {
13+
minimize: true,
14+
minimizer: [new TerserPlugin()],
15+
},
16+
};
17+
18+
export default merge(baseConfig, configuration);

config/webpack.config.renderer.base.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import HtmlWebpackPlugin from 'html-webpack-plugin';
44
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
55
import webpack from 'webpack';
66
import { merge } from 'webpack-merge';
7+
78
import baseConfig from './webpack.config.common';
89
import webpackPaths from './webpack.paths';
910

config/webpack.config.renderer.prod.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import CssMinimizerPlugin from 'css-minimizer-webpack-plugin';
22
import TerserPlugin from 'terser-webpack-plugin';
33
import type webpack from 'webpack';
44
import { merge } from 'webpack-merge';
5+
56
import baseConfig from './webpack.config.renderer.base';
67

78
const configuration: webpack.Configuration = {

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
"main": "build/main.js",
66
"scripts": {
77
"clean": "rimraf build coverage dist node_modules",
8-
"build": "concurrently --names \"main,renderer\" --prefix-colors \"blue,green\" \"pnpm build:main\" \"pnpm build:renderer\"",
8+
"build": "concurrently --names \"main,preload,renderer\" --prefix-colors \"blue,magenta,green\" \"pnpm build:main\" \"pnpm build:preload\" \"pnpm build:renderer\"",
99
"build:main": "webpack --config ./config/webpack.config.main.prod.ts",
10+
"build:preload": "webpack --config ./config/webpack.config.preload.prod.ts",
1011
"build:renderer": "webpack --config ./config/webpack.config.renderer.prod.ts",
11-
"watch": "concurrently --names \"main,renderer\" --prefix-colors \"blue,green\" \"pnpm watch:main\" \"pnpm watch:renderer\"",
12+
"watch": "concurrently --names \"main,preload,renderer\" --prefix-colors \"blue,magenta,green\" \"pnpm watch:main\" \"pnpm watch:preload\" \"pnpm watch:renderer\"",
1213
"watch:main": "webpack --watch --config ./config/webpack.config.main.base.ts",
14+
"watch:preload": "webpack --watch --config ./config/webpack.config.preload.base.ts",
1315
"watch:renderer": "webpack --watch --config ./config/webpack.config.renderer.base.ts",
1416
"prepare:remove-source-maps": "ts-node ./scripts/delete-source-maps.ts",
1517
"package:linux": "electron-builder --linux",

src/main/main.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import path from 'path';
12
import { app, globalShortcut, ipcMain as ipc, safeStorage } from 'electron';
23
import log from 'electron-log';
34
import { menubar } from 'menubar';
@@ -22,8 +23,10 @@ const browserWindowOpts = {
2223
skipTaskbar: true, // Hide the app from the Windows taskbar
2324
// TODO #700 refactor to use preload script with a context bridge
2425
webPreferences: {
25-
nodeIntegration: true,
26-
contextIsolation: false,
26+
preload: path.join(__dirname, 'preload.js'),
27+
contextIsolation: true, // TODO change this
28+
enableRemoteModule: false,
29+
nodeIntegration: false, // TODO change this
2730
},
2831
};
2932

@@ -125,7 +128,7 @@ app.whenReady().then(async () => {
125128

126129
ipc.on(namespacedEvent('window-hide'), () => mb.hideWindow());
127130

128-
ipc.on(namespacedEvent('quit'), () => mb.app.quit());
131+
// ipc.on(namespacedEvent('quit'), () => mb.app.quit());
129132

130133
ipc.on(
131134
namespacedEvent('use-alternate-idle-icon'),

src/main/preload.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { contextBridge, ipcRenderer } from 'electron';
2+
import { namespacedEvent } from '../shared/events';
3+
4+
import { Constants } from '../renderer/utils/constants';
5+
import type { GitifyAPI } from './types';
6+
7+
const api: GitifyAPI = {
8+
// Define the global variable
9+
global: globalThis,
10+
11+
openExternalLink: (url) =>
12+
ipcRenderer.send(namespacedEvent('open-external-link'), url),
13+
14+
getAppVersion: () => ipcRenderer.invoke(namespacedEvent('version')),
15+
16+
encryptValue: (value) =>
17+
ipcRenderer.invoke(namespacedEvent('safe-storage-encrypt'), value),
18+
19+
decryptValue: (value) =>
20+
ipcRenderer.invoke(namespacedEvent('safe-storage-decrypt'), value),
21+
22+
quitApp: () => ipcRenderer.send(namespacedEvent('quit')),
23+
24+
showWindow: () => ipcRenderer.send(namespacedEvent('window-show')),
25+
26+
hideWindow: () => ipcRenderer.send(namespacedEvent('window-hide')),
27+
28+
setAutoLaunch: (value) =>
29+
ipcRenderer.send(namespacedEvent('update-auto-launch'), {
30+
openAtLogin: value,
31+
openAsHidden: value,
32+
}),
33+
34+
setAlternateIdleIcon: (value) =>
35+
ipcRenderer.send(namespacedEvent('use-alternate-idle-icon'), value),
36+
37+
setKeyboardShortcut: (keyboardShortcut) =>
38+
ipcRenderer.send(namespacedEvent('update-keyboard-shortcut'), {
39+
enabled: keyboardShortcut,
40+
keyboardShortcut: Constants.DEFAULT_KEYBOARD_SHORTCUT,
41+
}),
42+
43+
updateTrayIcon: (notificationsLength = 0) => {
44+
if (notificationsLength < 0) {
45+
ipcRenderer.send(namespacedEvent('icon-error'));
46+
return;
47+
}
48+
49+
if (notificationsLength > 0) {
50+
ipcRenderer.send(namespacedEvent('icon-active'));
51+
return;
52+
}
53+
54+
ipcRenderer.send(namespacedEvent('icon-idle'));
55+
// ipcRenderer.send(namespacedEvent('update-tray-icon'), notificationsLength),
56+
},
57+
58+
updateTrayTitle: (title = '') =>
59+
ipcRenderer.send(namespacedEvent('update-title'), title),
60+
};
61+
62+
contextBridge.exposeInMainWorld('gitify', api);

src/main/types.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export interface GitifyAPI {
2+
global: typeof globalThis;
3+
openExternalLink: (url: string) => void;
4+
getAppVersion: () => Promise<string>;
5+
encryptValue: (value: string) => Promise<string>;
6+
decryptValue: (value: string) => Promise<string>;
7+
quitApp: () => void;
8+
showWindow: () => void;
9+
hideWindow: () => void;
10+
setAutoLaunch: (value: boolean) => void;
11+
setAlternateIdleIcon: (value: boolean) => void;
12+
setKeyboardShortcut: (keyboardShortcut: boolean) => void;
13+
updateTrayIcon: (notificationsLength?: number) => void;
14+
updateTrayTitle: (title?: string) => void;
15+
}

src/preload.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import type { GitifyAPI } from './main/types';
2+
3+
declare global {
4+
interface Window {
5+
gitify: GitifyAPI;
6+
}
7+
}

src/renderer/__mocks__/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
* Ensure stable snapshots for our randomized emoji use-cases
33
*/
44
export function ensureStableEmojis() {
5-
global.Math.random = jest.fn(() => 0.1);
5+
// global.Math.random = jest.fn(() => 0.1);
66
}

src/renderer/utils/comms.ts

Lines changed: 48 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { ipcRenderer, shell } from 'electron';
2-
import { namespacedEvent } from '../../shared/events';
1+
import { shell } from 'electron';
2+
// import { namespacedEvent } from '../../shared/events';
33
import { defaultSettings } from '../context/App';
44
import { type Link, OpenPreference } from '../types';
5-
import { Constants } from './constants';
5+
// import { Constants } from './constants';
66
import { loadState } from './storage';
77

88
export function openExternalLink(url: Link): void {
@@ -21,67 +21,79 @@ export function openExternalLink(url: Link): void {
2121
}
2222

2323
export async function getAppVersion(): Promise<string> {
24-
return await ipcRenderer.invoke(namespacedEvent('version'));
24+
return await window.gitify.getAppVersion();
25+
// return await ipcRenderer.invoke(namespacedEvent('version'));
2526
}
2627

2728
export async function encryptValue(value: string): Promise<string> {
28-
return await ipcRenderer.invoke(
29-
namespacedEvent('safe-storage-encrypt'),
30-
value,
31-
);
29+
return await window.gitify.encryptValue(value);
30+
31+
// return await ipcRenderer.invoke(
32+
// namespacedEvent('safe-storage-encrypt'),
33+
// value,
34+
// );
3235
}
3336

3437
export async function decryptValue(value: string): Promise<string> {
35-
return await ipcRenderer.invoke(
36-
namespacedEvent('safe-storage-decrypt'),
37-
value,
38-
);
38+
return await window.gitify.decryptValue(value);
39+
// return await ipcRenderer.invoke(
40+
// namespacedEvent('safe-storage-decrypt'),
41+
// value,
42+
// );
3943
}
4044

4145
export function quitApp(): void {
42-
ipcRenderer.send(namespacedEvent('quit'));
46+
window.gitify.quitApp();
47+
// ipcRenderer.send(namespacedEvent('quit'));
4348
}
4449

4550
export function showWindow(): void {
46-
ipcRenderer.send(namespacedEvent('window-show'));
51+
window.gitify.showWindow();
52+
// ipcRenderer.send(namespacedEvent('window-show'));
4753
}
4854

4955
export function hideWindow(): void {
50-
ipcRenderer.send(namespacedEvent('window-hide'));
56+
window.gitify.hideWindow();
57+
// ipcRenderer.send(namespacedEvent('window-hide'));
5158
}
5259

5360
export function setAutoLaunch(value: boolean): void {
54-
ipcRenderer.send(namespacedEvent('update-auto-launch'), {
55-
openAtLogin: value,
56-
openAsHidden: value,
57-
});
61+
window.gitify.setAutoLaunch(value);
62+
// ipcRenderer.send(namespacedEvent('update-auto-launch'), {
63+
// openAtLogin: value,
64+
// openAsHidden: value,
65+
// });
5866
}
5967

6068
export function setAlternateIdleIcon(value: boolean): void {
61-
ipcRenderer.send(namespacedEvent('use-alternate-idle-icon'), value);
69+
window.gitify.setAlternateIdleIcon(value);
70+
// ipcRenderer.send(namespacedEvent('use-alternate-idle-icon'), value);
6271
}
6372

6473
export function setKeyboardShortcut(keyboardShortcut: boolean): void {
65-
ipcRenderer.send(namespacedEvent('update-keyboard-shortcut'), {
66-
enabled: keyboardShortcut,
67-
keyboardShortcut: Constants.DEFAULT_KEYBOARD_SHORTCUT,
68-
});
74+
window.gitify.setKeyboardShortcut(keyboardShortcut);
75+
// ipcRenderer.send(namespacedEvent('update-keyboard-shortcut'), {
76+
// enabled: keyboardShortcut,
77+
// keyboardShortcut: Constants.DEFAULT_KEYBOARD_SHORTCUT,
78+
// });
6979
}
7080

7181
export function updateTrayIcon(notificationsLength = 0): void {
72-
if (notificationsLength < 0) {
73-
ipcRenderer.send(namespacedEvent('icon-error'));
74-
return;
75-
}
76-
77-
if (notificationsLength > 0) {
78-
ipcRenderer.send(namespacedEvent('icon-active'));
79-
return;
80-
}
81-
82-
ipcRenderer.send(namespacedEvent('icon-idle'));
82+
window.gitify.updateTrayIcon(notificationsLength);
83+
// if (notificationsLength < 0) {
84+
// ipcRenderer.send(namespacedEvent('icon-error'));
85+
// return;
86+
// }
87+
88+
// if (notificationsLength > 0) {
89+
// ipcRenderer.send(namespacedEvent('icon-active'));
90+
// return;
91+
// }
92+
93+
// ipcRenderer.send(namespacedEvent('icon-idle'));
8394
}
8495

8596
export function updateTrayTitle(title = ''): void {
86-
ipcRenderer.send(namespacedEvent('update-title'), title);
97+
window.gitify.updateTrayTitle(title);
98+
// ipcRenderer.send(namespacedEvent('update-title'), title);
8799
}

0 commit comments

Comments
 (0)