mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 15:53:27 +00:00
[PM-7489] Introduce MessageSender & MessageListener (#8709)
* Introduce MessageSender * Update `messageSenderFactory` * Remove Comment * Use BrowserApi * Update Comment * Rename to CommandDefinition * Add More Documentation to MessageSender * Add `EMPTY` helpers and remove NoopMessageSender * Calm Down Logging * Limit Logging On Known Errors * Use `messageStream` Parameter Co-authored-by: Matt Gibson <mgibson@bitwarden.com> * Add eslint rules * Update Error Handling Co-authored-by: Cesar Gonzalez <cesar.a.gonzalezcs@gmail.com> * Delete Lazy Classes In Favor of Observable Factories * Remove Fido Messages --------- Co-authored-by: Matt Gibson <mgibson@bitwarden.com> Co-authored-by: Cesar Gonzalez <cesar.a.gonzalezcs@gmail.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { APP_INITIALIZER, NgModule } from "@angular/core";
|
||||
import { Subject, merge } from "rxjs";
|
||||
|
||||
import { SafeProvider, safeProvider } from "@bitwarden/angular/platform/utils/safe-provider";
|
||||
import {
|
||||
@@ -14,6 +15,7 @@ import {
|
||||
SYSTEM_THEME_OBSERVABLE,
|
||||
SafeInjectionToken,
|
||||
STATE_FACTORY,
|
||||
INTRAPROCESS_MESSAGING_SUBJECT,
|
||||
} from "@bitwarden/angular/services/injection-tokens";
|
||||
import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module";
|
||||
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
|
||||
@@ -23,7 +25,6 @@ import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/ab
|
||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||
import { BroadcasterService as BroadcasterServiceAbstraction } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||
import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
||||
@@ -42,6 +43,9 @@ import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/
|
||||
import { SystemService as SystemServiceAbstraction } from "@bitwarden/common/platform/abstractions/system.service";
|
||||
import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
|
||||
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
|
||||
import { Message, MessageListener, MessageSender } from "@bitwarden/common/platform/messaging";
|
||||
// eslint-disable-next-line no-restricted-imports -- Used for dependency injection
|
||||
import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal";
|
||||
import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state";
|
||||
import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service";
|
||||
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner";
|
||||
@@ -63,11 +67,12 @@ import {
|
||||
ELECTRON_SUPPORTS_SECURE_STORAGE,
|
||||
ElectronPlatformUtilsService,
|
||||
} from "../../platform/services/electron-platform-utils.service";
|
||||
import { ElectronRendererMessagingService } from "../../platform/services/electron-renderer-messaging.service";
|
||||
import { ElectronRendererMessageSender } from "../../platform/services/electron-renderer-message.sender";
|
||||
import { ElectronRendererSecureStorageService } from "../../platform/services/electron-renderer-secure-storage.service";
|
||||
import { ElectronRendererStorageService } from "../../platform/services/electron-renderer-storage.service";
|
||||
import { ElectronStateService } from "../../platform/services/electron-state.service";
|
||||
import { I18nRendererService } from "../../platform/services/i18n.renderer.service";
|
||||
import { fromIpcMessaging } from "../../platform/utils/from-ipc-messaging";
|
||||
import { fromIpcSystemTheme } from "../../platform/utils/from-ipc-system-theme";
|
||||
import { EncryptedMessageHandlerService } from "../../services/encrypted-message-handler.service";
|
||||
import { NativeMessageHandlerService } from "../../services/native-message-handler.service";
|
||||
@@ -138,9 +143,24 @@ const safeProviders: SafeProvider[] = [
|
||||
deps: [SYSTEM_LANGUAGE, LOCALES_DIRECTORY, GlobalStateProvider],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: MessagingServiceAbstraction,
|
||||
useClass: ElectronRendererMessagingService,
|
||||
deps: [BroadcasterServiceAbstraction],
|
||||
provide: MessageSender,
|
||||
useFactory: (subject: Subject<Message<object>>) =>
|
||||
MessageSender.combine(
|
||||
new ElectronRendererMessageSender(), // Communication with main process
|
||||
new SubjectMessageSender(subject), // Communication with ourself
|
||||
),
|
||||
deps: [INTRAPROCESS_MESSAGING_SUBJECT],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: MessageListener,
|
||||
useFactory: (subject: Subject<Message<object>>) =>
|
||||
new MessageListener(
|
||||
merge(
|
||||
subject.asObservable(), // For messages from the same context
|
||||
fromIpcMessaging(), // For messages from the main process
|
||||
),
|
||||
),
|
||||
deps: [INTRAPROCESS_MESSAGING_SUBJECT],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: AbstractStorageService,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as path from "path";
|
||||
|
||||
import { app } from "electron";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
import { Subject, firstValueFrom } from "rxjs";
|
||||
|
||||
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service";
|
||||
@@ -11,6 +11,9 @@ import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwar
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { DefaultBiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
|
||||
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
|
||||
import { Message, MessageSender } from "@bitwarden/common/platform/messaging";
|
||||
// eslint-disable-next-line no-restricted-imports -- For dependency creation
|
||||
import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal";
|
||||
import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state";
|
||||
import { EncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/encrypt.service.implementation";
|
||||
import { DefaultEnvironmentService } from "@bitwarden/common/platform/services/default-environment.service";
|
||||
@@ -18,7 +21,6 @@ import { KeyGenerationService } from "@bitwarden/common/platform/services/key-ge
|
||||
import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service";
|
||||
import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service";
|
||||
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner";
|
||||
import { NoopMessagingService } from "@bitwarden/common/platform/services/noop-messaging.service";
|
||||
/* eslint-disable import/no-restricted-paths -- We need the implementation to inject, but generally this should not be accessed */
|
||||
import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider";
|
||||
import { DefaultActiveUserStateProvider } from "@bitwarden/common/platform/state/implementations/default-active-user-state.provider";
|
||||
@@ -59,7 +61,7 @@ export class Main {
|
||||
storageService: ElectronStorageService;
|
||||
memoryStorageService: MemoryStorageService;
|
||||
memoryStorageForStateProviders: MemoryStorageServiceForStateProviders;
|
||||
messagingService: ElectronMainMessagingService;
|
||||
messagingService: MessageSender;
|
||||
stateService: StateService;
|
||||
environmentService: DefaultEnvironmentService;
|
||||
mainCryptoFunctionService: MainCryptoFunctionService;
|
||||
@@ -131,7 +133,7 @@ export class Main {
|
||||
this.i18nService = new I18nMainService("en", "./locales/", globalStateProvider);
|
||||
|
||||
const accountService = new AccountServiceImplementation(
|
||||
new NoopMessagingService(),
|
||||
MessageSender.EMPTY,
|
||||
this.logService,
|
||||
globalStateProvider,
|
||||
);
|
||||
@@ -223,7 +225,13 @@ export class Main {
|
||||
this.updaterMain = new UpdaterMain(this.i18nService, this.windowMain);
|
||||
this.trayMain = new TrayMain(this.windowMain, this.i18nService, this.desktopSettingsService);
|
||||
|
||||
this.messagingService = new ElectronMainMessagingService(this.windowMain, (message) => {
|
||||
const messageSubject = new Subject<Message<object>>();
|
||||
this.messagingService = MessageSender.combine(
|
||||
new SubjectMessageSender(messageSubject), // For local messages
|
||||
new ElectronMainMessagingService(this.windowMain),
|
||||
);
|
||||
|
||||
messageSubject.asObservable().subscribe((message) => {
|
||||
this.messagingMain.onMessage(message);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { powerMonitor } from "electron";
|
||||
|
||||
import { ElectronMainMessagingService } from "../services/electron-main-messaging.service";
|
||||
import { MessageSender } from "@bitwarden/common/platform/messaging";
|
||||
|
||||
import { isSnapStore } from "../utils";
|
||||
|
||||
// tslint:disable-next-line
|
||||
@@ -10,7 +11,7 @@ const IdleCheckInterval = 30 * 1000; // 30 seconds
|
||||
export class PowerMonitorMain {
|
||||
private idle = false;
|
||||
|
||||
constructor(private messagingService: ElectronMainMessagingService) {}
|
||||
constructor(private messagingService: MessageSender) {}
|
||||
|
||||
init() {
|
||||
// ref: https://github.com/electron/electron/issues/13767
|
||||
|
||||
@@ -124,12 +124,21 @@ export default {
|
||||
|
||||
sendMessage: (message: { command: string } & any) =>
|
||||
ipcRenderer.send("messagingService", message),
|
||||
onMessage: (callback: (message: { command: string } & any) => void) => {
|
||||
ipcRenderer.on("messagingService", (_event, message: any) => {
|
||||
if (message.command) {
|
||||
callback(message);
|
||||
}
|
||||
});
|
||||
onMessage: {
|
||||
addListener: (callback: (message: { command: string } & any) => void) => {
|
||||
ipcRenderer.addListener("messagingService", (_event, message: any) => {
|
||||
if (message.command) {
|
||||
callback(message);
|
||||
}
|
||||
});
|
||||
},
|
||||
removeListener: (callback: (message: { command: string } & any) => void) => {
|
||||
ipcRenderer.removeListener("messagingService", (_event, message: any) => {
|
||||
if (message.command) {
|
||||
callback(message);
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
launchUri: (uri: string) => ipcRenderer.invoke("launchUri", uri),
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { MessageSender, CommandDefinition } from "@bitwarden/common/platform/messaging";
|
||||
import { getCommand } from "@bitwarden/common/platform/messaging/internal";
|
||||
|
||||
export class ElectronRendererMessageSender implements MessageSender {
|
||||
send<T extends object>(
|
||||
commandDefinition: CommandDefinition<T> | string,
|
||||
payload: object | T = {},
|
||||
): void {
|
||||
const command = getCommand(commandDefinition);
|
||||
ipc.platform.sendMessage(Object.assign({}, { command: command }, payload));
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
|
||||
export class ElectronRendererMessagingService implements MessagingService {
|
||||
constructor(private broadcasterService: BroadcasterService) {
|
||||
ipc.platform.onMessage((message) => this.sendMessage(message.command, message, false));
|
||||
}
|
||||
|
||||
send(subscriber: string, arg: any = {}) {
|
||||
this.sendMessage(subscriber, arg, true);
|
||||
}
|
||||
|
||||
private sendMessage(subscriber: string, arg: any = {}, toMain: boolean) {
|
||||
const message = Object.assign({}, { command: subscriber }, arg);
|
||||
this.broadcasterService.send(message);
|
||||
if (toMain) {
|
||||
ipc.platform.sendMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
15
apps/desktop/src/platform/utils/from-ipc-messaging.ts
Normal file
15
apps/desktop/src/platform/utils/from-ipc-messaging.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { fromEventPattern, share } from "rxjs";
|
||||
|
||||
import { Message } from "@bitwarden/common/platform/messaging";
|
||||
import { tagAsExternal } from "@bitwarden/common/platform/messaging/internal";
|
||||
|
||||
/**
|
||||
* Creates an observable that when subscribed to will listen to messaging events through IPC.
|
||||
* @returns An observable stream of messages.
|
||||
*/
|
||||
export const fromIpcMessaging = () => {
|
||||
return fromEventPattern<Message<object>>(
|
||||
(handler) => ipc.platform.onMessage.addListener(handler),
|
||||
(handler) => ipc.platform.onMessage.removeListener(handler),
|
||||
).pipe(tagAsExternal, share());
|
||||
};
|
||||
@@ -2,18 +2,17 @@ import * as path from "path";
|
||||
|
||||
import { app, dialog, ipcMain, Menu, MenuItem, nativeTheme, Notification, shell } from "electron";
|
||||
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { ThemeType } from "@bitwarden/common/platform/enums";
|
||||
import { MessageSender, CommandDefinition } from "@bitwarden/common/platform/messaging";
|
||||
// eslint-disable-next-line no-restricted-imports -- Using implementation helper in implementation
|
||||
import { getCommand } from "@bitwarden/common/platform/messaging/internal";
|
||||
import { SafeUrls } from "@bitwarden/common/platform/misc/safe-urls";
|
||||
|
||||
import { WindowMain } from "../main/window.main";
|
||||
import { RendererMenuItem } from "../utils";
|
||||
|
||||
export class ElectronMainMessagingService implements MessagingService {
|
||||
constructor(
|
||||
private windowMain: WindowMain,
|
||||
private onMessage: (message: any) => void,
|
||||
) {
|
||||
export class ElectronMainMessagingService implements MessageSender {
|
||||
constructor(private windowMain: WindowMain) {
|
||||
ipcMain.handle("appVersion", () => {
|
||||
return app.getVersion();
|
||||
});
|
||||
@@ -88,9 +87,9 @@ export class ElectronMainMessagingService implements MessagingService {
|
||||
});
|
||||
}
|
||||
|
||||
send(subscriber: string, arg: any = {}) {
|
||||
const message = Object.assign({}, { command: subscriber }, arg);
|
||||
this.onMessage(message);
|
||||
send<T extends object>(commandDefinition: CommandDefinition<T> | string, arg: T | object = {}) {
|
||||
const command = getCommand(commandDefinition);
|
||||
const message = Object.assign({}, { command: command }, arg);
|
||||
if (this.windowMain.win != null) {
|
||||
this.windowMain.win.webContents.send("messagingService", message);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user