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:
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
8
package-lock.json
generated
@@ -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": {
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user