From 78d40d9f18c23a185465d5fca238b258b2848193 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Wed, 7 Apr 2021 19:42:06 +0200 Subject: [PATCH] Remove electron remote (#332) * Replace remote calls with ipcRenderer.invoke. --- src/abstractions/platformUtils.service.ts | 4 +- src/cli/commands/update.command.ts | 2 +- src/cli/services/cliPlatformUtils.service.ts | 6 +-- .../services/electronMainMessaging.service.ts | 50 ++++++++++++++++++- .../services/electronPlatformUtils.service.ts | 26 +++------- .../electronRendererStorage.service.ts | 27 ++++++++++ .../services/electronStorage.service.ts | 12 +++++ src/electron/utils.ts | 15 ++++++ src/misc/analytics.ts | 2 +- 9 files changed, 118 insertions(+), 26 deletions(-) create mode 100644 src/electron/services/electronRendererStorage.service.ts diff --git a/src/abstractions/platformUtils.service.ts b/src/abstractions/platformUtils.service.ts index 415151d3ac..acf11779c4 100644 --- a/src/abstractions/platformUtils.service.ts +++ b/src/abstractions/platformUtils.service.ts @@ -20,7 +20,7 @@ export abstract class PlatformUtilsService { lockTimeout: () => number; launchUri: (uri: string, options?: any) => void; saveFile: (win: Window, blobData: any, blobOptions: any, fileName: string) => void; - getApplicationVersion: () => string; + getApplicationVersion: () => Promise; supportsWebAuthn: (win: Window) => boolean; supportsDuo: () => boolean; showToast: (type: 'error' | 'success' | 'warning' | 'info', title: string, text: string | string[], @@ -34,7 +34,7 @@ export abstract class PlatformUtilsService { readFromClipboard: (options?: any) => Promise; supportsBiometric: () => Promise; authenticateBiometric: () => Promise; - getDefaultSystemTheme: () => 'light' | 'dark'; + getDefaultSystemTheme: () => Promise<'light' | 'dark'>; onDefaultSystemThemeChange: (callback: ((theme: 'light' | 'dark') => unknown)) => unknown; supportsSecureStorage: () => boolean; } diff --git a/src/cli/commands/update.command.ts b/src/cli/commands/update.command.ts index 4dc1c58893..edca6a31a9 100644 --- a/src/cli/commands/update.command.ts +++ b/src/cli/commands/update.command.ts @@ -16,7 +16,7 @@ export class UpdateCommand { } async run(): Promise { - const currentVersion = this.platformUtilsService.getApplicationVersion(); + const currentVersion = await this.platformUtilsService.getApplicationVersion(); const response = await fetch.default('https://api.github.com/repos/bitwarden/' + this.repoName + '/releases/latest'); diff --git a/src/cli/services/cliPlatformUtils.service.ts b/src/cli/services/cliPlatformUtils.service.ts index bba2937633..8d8dba05af 100644 --- a/src/cli/services/cliPlatformUtils.service.ts +++ b/src/cli/services/cliPlatformUtils.service.ts @@ -96,8 +96,8 @@ export class CliPlatformUtilsService implements PlatformUtilsService { throw new Error('Not implemented.'); } - getApplicationVersion(): string { - return this.packageJson.version; + getApplicationVersion(): Promise { + return Promise.resolve(this.packageJson.version); } supportsWebAuthn(win: Window) { @@ -147,7 +147,7 @@ export class CliPlatformUtilsService implements PlatformUtilsService { } getDefaultSystemTheme() { - return 'light' as 'light' | 'dark'; + return Promise.resolve('light' as 'light' | 'dark'); } onDefaultSystemThemeChange() { diff --git a/src/electron/services/electronMainMessaging.service.ts b/src/electron/services/electronMainMessaging.service.ts index 6e366147da..c620fd660b 100644 --- a/src/electron/services/electronMainMessaging.service.ts +++ b/src/electron/services/electronMainMessaging.service.ts @@ -1,9 +1,57 @@ +import { app, dialog, ipcMain, Menu, MenuItem, nativeTheme } from 'electron'; +import { promises as fs } from 'fs'; import { MessagingService } from '../../abstractions/messaging.service'; +import { RendererMenuItem } from '../utils'; import { WindowMain } from '../window.main'; export class ElectronMainMessagingService implements MessagingService { - constructor(private windowMain: WindowMain, private onMessage: (message: any) => void) { } + constructor(private windowMain: WindowMain, private onMessage: (message: any) => void) { + ipcMain.handle('appVersion', () => { + return app.getVersion(); + }); + + ipcMain.handle('systemTheme', () => { + return nativeTheme.shouldUseDarkColors ? 'dark' : 'light'; + }); + + ipcMain.handle('showMessageBox', (event, options) => { + return dialog.showMessageBox(options); + }); + + ipcMain.handle('saveFile', (event, options) => { + dialog.showSaveDialog(windowMain.win, { + defaultPath: options.fileName, + showsTagField: false, + }).then(ret => { + if (ret.filePath != null) { + fs.writeFile(ret.filePath, options.buffer, { mode: 0o600 }); + } + }); + }); + + ipcMain.handle('openContextMenu', (event, options: {menu: RendererMenuItem[]}) => { + return new Promise(resolve => { + const menu = new Menu(); + options.menu.forEach((m, index) => { + menu.append(new MenuItem({ + label: m.label, + type: m.type, + click: () => { + resolve(index); + }, + })); + }); + menu.popup({ window: windowMain.win, callback: () => { + resolve(-1); + }}); + }); + }); + + nativeTheme.on('updated', () => { + windowMain.win.webContents.send('systemThemeUpdated', nativeTheme.shouldUseDarkColors ? 'dark' : 'light'); + }); + } send(subscriber: string, arg: any = {}) { const message = Object.assign({}, { command: subscriber }, arg); diff --git a/src/electron/services/electronPlatformUtils.service.ts b/src/electron/services/electronPlatformUtils.service.ts index e15a6b1d63..f7d92e6e14 100644 --- a/src/electron/services/electronPlatformUtils.service.ts +++ b/src/electron/services/electronPlatformUtils.service.ts @@ -1,10 +1,8 @@ import { clipboard, ipcRenderer, - remote, shell, } from 'electron'; -import * as fs from 'fs'; import { isDev, @@ -114,20 +112,14 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService { } saveFile(win: Window, blobData: any, blobOptions: any, fileName: string): void { - remote.dialog.showSaveDialog(remote.getCurrentWindow(), { - defaultPath: fileName, - showsTagField: false, - }).then(ret => { - if (ret.filePath != null) { - fs.writeFile(ret.filePath, Buffer.from(blobData), { mode: 0o600 }, err => { - // error check? - }); - } + ipcRenderer.invoke('saveFile', { + fileName: fileName, + buffer: Buffer.from(blobData), }); } - getApplicationVersion(): string { - return remote.app.getVersion(); + getApplicationVersion(): Promise { + return ipcRenderer.invoke('appVersion'); } // Temporarily restricted to only Windows until https://github.com/electron/electron/pull/28349 @@ -157,7 +149,7 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService { buttons.push(cancelText); } - const result = await remote.dialog.showMessageBox(remote.getCurrentWindow(), { + const result = await ipcRenderer.invoke('showMessageBox', { type: type, title: title, message: title, @@ -221,13 +213,11 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService { } getDefaultSystemTheme() { - return remote.nativeTheme.shouldUseDarkColors ? 'dark' : 'light'; + return ipcRenderer.invoke('systemTheme'); } onDefaultSystemThemeChange(callback: ((theme: 'light' | 'dark') => unknown)) { - remote.nativeTheme.on('updated', () => { - callback(this.getDefaultSystemTheme()); - }); + ipcRenderer.on('systemThemeUpdated', (event, theme: 'light' | 'dark') => callback(theme)); } supportsSecureStorage(): boolean { diff --git a/src/electron/services/electronRendererStorage.service.ts b/src/electron/services/electronRendererStorage.service.ts new file mode 100644 index 0000000000..bf9c3241e0 --- /dev/null +++ b/src/electron/services/electronRendererStorage.service.ts @@ -0,0 +1,27 @@ +import { ipcRenderer } from 'electron'; + +import { StorageService } from '../../abstractions/storage.service'; + +export class ElectronRendererStorageService implements StorageService { + get(key: string): Promise { + return ipcRenderer.invoke('storageService', { + action: 'get', + key: key, + }); + } + + save(key: string, obj: any): Promise { + return ipcRenderer.invoke('storageService', { + action: 'save', + key: key, + obj: obj, + }); + } + + remove(key: string): Promise { + return ipcRenderer.invoke('storageService', { + action: 'remove', + key: key, + }); + } +} diff --git a/src/electron/services/electronStorage.service.ts b/src/electron/services/electronStorage.service.ts index 3cc21b5eea..6cf5fec57c 100644 --- a/src/electron/services/electronStorage.service.ts +++ b/src/electron/services/electronStorage.service.ts @@ -1,3 +1,4 @@ +import { ipcMain, ipcRenderer } from 'electron'; import * as fs from 'fs'; import { StorageService } from '../../abstractions/storage.service'; @@ -19,6 +20,17 @@ export class ElectronStorageService implements StorageService { name: 'data', }; this.store = new Store(storeConfig); + + ipcMain.handle('storageService', (event, options) => { + switch (options.action) { + case 'get': + return this.get(options.key); + case 'save': + return this.save(options.key, options.obj); + case 'remove': + return this.remove(options.key); + } + }); } get(key: string): Promise { diff --git a/src/electron/utils.ts b/src/electron/utils.ts index f9d72ee721..80eb09794f 100644 --- a/src/electron/utils.ts +++ b/src/electron/utils.ts @@ -1,3 +1,18 @@ +import { ipcRenderer } from 'electron'; + +export type RendererMenuItem = {label?: string, type?: ('normal' | 'separator' | 'submenu' | 'checkbox' | 'radio'), click?: () => any}; + +export function invokeMenu(menu: RendererMenuItem[]) { + const menuWithoutClick = menu.map(m => { + return { label: m.label, type: m.type }; + }); + ipcRenderer.invoke('openContextMenu', { menu: menuWithoutClick }).then((i: number) => { + if (i !== -1) { + menu[i].click(); + } + }); +} + export function isDev() { // ref: https://github.com/sindresorhus/electron-is-dev if ('ELECTRON_IS_DEV' in process.env) { diff --git a/src/misc/analytics.ts b/src/misc/analytics.ts index 970e4dee73..9fa2276cee 100644 --- a/src/misc/analytics.ts +++ b/src/misc/analytics.ts @@ -41,7 +41,7 @@ export class Analytics { } } - this.appVersion = this.platformUtilsService.getApplicationVersion(); + this.platformUtilsService.getApplicationVersion().then(v => this.appVersion = v); this.defaultDisabled = this.platformUtilsService.getDevice() === DeviceType.FirefoxExtension || this.platformUtilsService.isMacAppStore(); this.gaTrackingId = this.platformUtilsService.analyticsId();