1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-12 14:23:32 +00:00

[PM-18044] Ensure all calls to receive should get all messages broadcast (#13869)

* feat: make compatible with SDK changes

* feat: use subscription

* feat: update SDK

* fix: lint

* fix: ts strict issues
This commit is contained in:
Andreas Coroiu
2025-05-05 18:19:41 +02:00
committed by GitHub
parent 60bafc1311
commit e0cabd1df0
8 changed files with 106 additions and 113 deletions

View File

@@ -1,45 +0,0 @@
import { IpcMessage, isIpcMessage } from "@bitwarden/common/platform/ipc";
import { MessageQueue } from "@bitwarden/common/platform/ipc/message-queue";
import { CommunicationBackend, IncomingMessage, OutgoingMessage } from "@bitwarden/sdk-internal";
import { BrowserApi } from "../browser/browser-api";
export class BackgroundCommunicationBackend implements CommunicationBackend {
private queue = new MessageQueue<IncomingMessage>();
constructor() {
BrowserApi.messageListener("platform.ipc", (message, sender) => {
if (!isIpcMessage(message)) {
return;
}
if (sender.tab?.id === undefined || sender.tab.id === chrome.tabs.TAB_ID_NONE) {
// Ignore messages from non-tab sources
return;
}
void this.queue.enqueue(
new IncomingMessage(message.message.payload, message.message.destination, {
Web: { id: sender.tab.id },
}),
);
});
}
async send(message: OutgoingMessage): Promise<void> {
if (typeof message.destination === "object" && "Web" in message.destination) {
await BrowserApi.tabSendMessage(
{ id: message.destination.Web.id } as chrome.tabs.Tab,
{ type: "bitwarden-ipc-message", message } satisfies IpcMessage,
{ frameId: 0 },
);
return;
}
throw new Error("Destination not supported.");
}
async receive(): Promise<IncomingMessage> {
return this.queue.dequeue();
}
}

View File

@@ -1,12 +1,17 @@
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service";
import { IpcService } from "@bitwarden/common/platform/ipc"; import { IpcMessage, isIpcMessage, IpcService } from "@bitwarden/common/platform/ipc";
import { IpcClient } from "@bitwarden/sdk-internal"; import {
IpcClient,
IpcCommunicationBackend,
IncomingMessage,
OutgoingMessage,
} from "@bitwarden/sdk-internal";
import { BackgroundCommunicationBackend } from "./background-communication-backend"; import { BrowserApi } from "../browser/browser-api";
export class IpcBackgroundService extends IpcService { export class IpcBackgroundService extends IpcService {
private communicationProvider?: BackgroundCommunicationBackend; private communicationBackend?: IpcCommunicationBackend;
constructor(private logService: LogService) { constructor(private logService: LogService) {
super(); super();
@@ -16,9 +21,46 @@ export class IpcBackgroundService extends IpcService {
try { try {
// This function uses classes and functions defined in the SDK, so we need to wait for the SDK to load. // This function uses classes and functions defined in the SDK, so we need to wait for the SDK to load.
await SdkLoadService.Ready; await SdkLoadService.Ready;
this.communicationProvider = new BackgroundCommunicationBackend(); this.communicationBackend = new IpcCommunicationBackend({
async send(message: OutgoingMessage): Promise<void> {
if (typeof message.destination === "object") {
await BrowserApi.tabSendMessage(
{ id: message.destination.Web.id } as chrome.tabs.Tab,
{
type: "bitwarden-ipc-message",
message: {
destination: message.destination,
payload: message.payload,
topic: message.topic,
},
} satisfies IpcMessage,
{ frameId: 0 },
);
return;
}
await super.initWithClient(new IpcClient(this.communicationProvider)); throw new Error("Destination not supported.");
},
});
BrowserApi.messageListener("platform.ipc", (message, sender) => {
if (!isIpcMessage(message)) {
return;
}
if (sender.tab?.id === undefined || sender.tab.id === chrome.tabs.TAB_ID_NONE) {
// Ignore messages from non-tab sources
return;
}
this.communicationBackend?.deliver_message(
new IncomingMessage(message.message.payload, message.message.destination, {
Web: { id: sender.tab.id },
}),
);
});
await super.initWithClient(new IpcClient(this.communicationBackend));
} catch (e) { } catch (e) {
this.logService.error("[IPC] Initialization failed", e); this.logService.error("[IPC] Initialization failed", e);
} }

View File

@@ -1,47 +0,0 @@
import { Injectable } from "@angular/core";
import { IpcMessage, isIpcMessage } from "@bitwarden/common/platform/ipc";
import { MessageQueue } from "@bitwarden/common/platform/ipc/message-queue";
import { CommunicationBackend, IncomingMessage, OutgoingMessage } from "@bitwarden/sdk-internal";
@Injectable({ providedIn: "root" })
export class WebCommunicationProvider implements CommunicationBackend {
private queue = new MessageQueue<IncomingMessage>();
constructor() {
window.addEventListener("message", async (event: MessageEvent) => {
if (event.origin !== window.origin) {
return;
}
const message = event.data;
if (!isIpcMessage(message)) {
return;
}
void this.queue.enqueue(
new IncomingMessage(
message.message.payload,
message.message.destination,
"BrowserBackground",
),
);
});
}
async send(message: OutgoingMessage): Promise<void> {
if (message.destination === "BrowserBackground") {
window.postMessage(
{ type: "bitwarden-ipc-message", message } satisfies IpcMessage,
window.location.origin,
);
return;
}
throw new Error(`Destination not supported: ${message.destination}`);
}
receive(): Promise<IncomingMessage> {
return this.queue.dequeue();
}
}

View File

@@ -2,22 +2,65 @@ import { inject } from "@angular/core";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service";
import { IpcService } from "@bitwarden/common/platform/ipc"; import { IpcMessage, IpcService, isIpcMessage } from "@bitwarden/common/platform/ipc";
import { IpcClient } from "@bitwarden/sdk-internal"; import {
IncomingMessage,
import { WebCommunicationProvider } from "./web-communication-provider"; IpcClient,
IpcCommunicationBackend,
OutgoingMessage,
} from "@bitwarden/sdk-internal";
export class WebIpcService extends IpcService { export class WebIpcService extends IpcService {
private logService = inject(LogService); private logService = inject(LogService);
private communicationProvider?: WebCommunicationProvider; private communicationBackend?: IpcCommunicationBackend;
override async init() { override async init() {
try { try {
// This function uses classes and functions defined in the SDK, so we need to wait for the SDK to load. // This function uses classes and functions defined in the SDK, so we need to wait for the SDK to load.
await SdkLoadService.Ready; await SdkLoadService.Ready;
this.communicationProvider = new WebCommunicationProvider();
await super.initWithClient(new IpcClient(this.communicationProvider)); this.communicationBackend = new IpcCommunicationBackend({
async send(message: OutgoingMessage): Promise<void> {
if (message.destination === "BrowserBackground") {
window.postMessage(
{
type: "bitwarden-ipc-message",
message: {
destination: message.destination,
payload: message.payload,
topic: message.topic,
},
} satisfies IpcMessage,
window.location.origin,
);
return;
}
throw new Error(`Destination not supported: ${message.destination}`);
},
});
window.addEventListener("message", async (event: MessageEvent) => {
if (event.origin !== window.origin) {
return;
}
const message = event.data;
if (!isIpcMessage(message)) {
return;
}
this.communicationBackend?.deliver_message(
new IncomingMessage(
message.message.payload,
message.message.destination,
"BrowserBackground",
message.message.topic,
),
);
});
await super.initWithClient(new IpcClient(this.communicationBackend));
} catch (e) { } catch (e) {
this.logService.error("[IPC] Initialization failed", e); this.logService.error("[IPC] Initialization failed", e);
} }

View File

@@ -2,7 +2,7 @@ import type { OutgoingMessage } from "@bitwarden/sdk-internal";
export interface IpcMessage { export interface IpcMessage {
type: "bitwarden-ipc-message"; type: "bitwarden-ipc-message";
message: OutgoingMessage; message: Omit<OutgoingMessage, "free">;
} }
export function isIpcMessage(message: any): message is IpcMessage { export function isIpcMessage(message: any): message is IpcMessage {

View File

@@ -25,11 +25,11 @@ export abstract class IpcService {
this._client = client; this._client = client;
this._messages$ = new Observable<IncomingMessage>((subscriber) => { this._messages$ = new Observable<IncomingMessage>((subscriber) => {
let isSubscribed = true; let isSubscribed = true;
const receiveLoop = async () => { const receiveLoop = async () => {
const subscription = await this.client.subscribe();
while (isSubscribed) { while (isSubscribed) {
try { try {
const message = await this.client.receive(); const message = await subscription.receive();
subscriber.next(message); subscriber.next(message);
} catch (error) { } catch (error) {
subscriber.error(error); subscriber.error(error);

8
package-lock.json generated
View File

@@ -24,7 +24,7 @@
"@angular/platform-browser": "18.2.13", "@angular/platform-browser": "18.2.13",
"@angular/platform-browser-dynamic": "18.2.13", "@angular/platform-browser-dynamic": "18.2.13",
"@angular/router": "18.2.13", "@angular/router": "18.2.13",
"@bitwarden/sdk-internal": "0.2.0-main.137", "@bitwarden/sdk-internal": "0.2.0-main.159",
"@electron/fuses": "1.8.0", "@electron/fuses": "1.8.0",
"@emotion/css": "11.13.5", "@emotion/css": "11.13.5",
"@koa/multer": "3.0.2", "@koa/multer": "3.0.2",
@@ -4700,9 +4700,9 @@
"link": true "link": true
}, },
"node_modules/@bitwarden/sdk-internal": { "node_modules/@bitwarden/sdk-internal": {
"version": "0.2.0-main.137", "version": "0.2.0-main.159",
"resolved": "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-0.2.0-main.137.tgz", "resolved": "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-0.2.0-main.159.tgz",
"integrity": "sha512-Df0pB5tOEc4WiMjskunTrqHulPzenFv8C61sqsBhHfy80xcf5kU5JyPd4asbf3e4uNS6QGXptd8imp09AuiFEA==", "integrity": "sha512-vliX5w/A6fuKWZJpDZTCPV4EU5CFrrs6zAv0aQaUQXF9LqL1YVh113D1NhOMuG2ILLWs2kDcTKiprvWFSTu1dg==",
"license": "GPL-3.0" "license": "GPL-3.0"
}, },
"node_modules/@bitwarden/send-ui": { "node_modules/@bitwarden/send-ui": {

View File

@@ -156,7 +156,7 @@
"@angular/platform-browser": "18.2.13", "@angular/platform-browser": "18.2.13",
"@angular/platform-browser-dynamic": "18.2.13", "@angular/platform-browser-dynamic": "18.2.13",
"@angular/router": "18.2.13", "@angular/router": "18.2.13",
"@bitwarden/sdk-internal": "0.2.0-main.137", "@bitwarden/sdk-internal": "0.2.0-main.159",
"@electron/fuses": "1.8.0", "@electron/fuses": "1.8.0",
"@emotion/css": "11.13.5", "@emotion/css": "11.13.5",
"@koa/multer": "3.0.2", "@koa/multer": "3.0.2",