1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-21 02:33:46 +00:00

[PM-18039] Add initial verison of IpcServices to client (#13373)

* feat: add foreground ipc service

* refactor: create abstract ipc service in libs

* wip: remove IPC service complexity

The code was making some wrong assumptions about how IPC is going to work. I'm removing everything and starting the content-script instead

* feat: working message sending from page to background

* refactor: move into common

* feat: somewhat complete web <-> browser link

* wip: ping command from web

* fix: import path

* fix: wip urls

* wip: add console log

* feat: successfull message sending (not receiving)

* feat: implement IPC using new refactored framework

* wip: add some console logs

* wip: almost working ping/pong

* feat: working ping/pong

* chore: clean-up ping/pong and some console logs

* chore: remove unused file

* fix: override lint rule

* chore: remove unused ping message

* feat: add tests for message queue

* fix: adapt to name changes and modifications to SDK branch

* fix: missing import

* fix: remove content script from manifest

The feature is not ready for prodution code yet. We will add dynamic injection with feature-flag support in a follow-up PR

* fix: remove fileless lp

* fix: make same changes to manifest v2

* fix: initialization functions

Add missing error handling, wait for the SDK to load and properly depend on the log service

* feat: use named id field

* chore: update sdk version to include IPC changes

* fix: remove messages$ buffer

* fix: forgot to commit package-lock

* feat: add additional destination check

* feat: only import type in ipc-message

* fix: typing issues

* feat: check message origin
This commit is contained in:
Andreas Coroiu
2025-04-08 15:06:39 +02:00
committed by GitHub
parent b488253722
commit 772b42f5b5
16 changed files with 340 additions and 6 deletions

View File

@@ -111,6 +111,7 @@ import {
} from "@bitwarden/common/platform/abstractions/storage.service";
import { SystemService as SystemServiceAbstraction } from "@bitwarden/common/platform/abstractions/system.service";
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
import { IpcService } from "@bitwarden/common/platform/ipc";
import { Message, MessageListener, MessageSender } from "@bitwarden/common/platform/messaging";
// eslint-disable-next-line no-restricted-imports -- Used for dependency creation
import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal";
@@ -259,6 +260,7 @@ import { BackgroundBrowserBiometricsService } from "../key-management/biometrics
import VaultTimeoutService from "../key-management/vault-timeout/vault-timeout.service";
import { BrowserApi } from "../platform/browser/browser-api";
import { flagEnabled } from "../platform/flags";
import { IpcBackgroundService } from "../platform/ipc/ipc-background.service";
import { UpdateBadge } from "../platform/listeners/update-badge";
/* eslint-disable no-restricted-imports */
import { ChromeMessageSender } from "../platform/messaging/chrome-message.sender";
@@ -403,6 +405,8 @@ export default class MainBackground {
inlineMenuFieldQualificationService: InlineMenuFieldQualificationService;
taskService: TaskService;
ipcService: IpcService;
onUpdatedRan: boolean;
onReplacedRan: boolean;
loginToAutoFill: CipherView = null;
@@ -1309,6 +1313,8 @@ export default class MainBackground {
);
this.inlineMenuFieldQualificationService = new InlineMenuFieldQualificationService();
this.ipcService = new IpcBackgroundService(this.logService);
}
async bootstrap() {
@@ -1382,6 +1388,7 @@ export default class MainBackground {
}
await this.initOverlayAndTabsBackground();
await this.ipcService.init();
return new Promise<void>((resolve) => {
setTimeout(async () => {

View File

@@ -0,0 +1,41 @@
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({ ...message.message, source: { 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

@@ -0,0 +1,51 @@
// TODO: This content script should be dynamically reloaded when the extension is updated,
// to avoid "Extension context invalidated." errors.
import { isIpcMessage } from "@bitwarden/common/platform/ipc/ipc-message";
// Web -> Background
export function sendExtensionMessage(message: unknown) {
if (
typeof browser !== "undefined" &&
typeof browser.runtime !== "undefined" &&
typeof browser.runtime.sendMessage !== "undefined"
) {
void browser.runtime.sendMessage(message);
return;
}
void chrome.runtime.sendMessage(message);
}
window.addEventListener("message", (event) => {
if (event.origin !== window.origin) {
return;
}
if (isIpcMessage(event.data)) {
sendExtensionMessage(event.data);
}
});
// Background -> Web
function setupMessageListener() {
function listener(message: unknown) {
if (isIpcMessage(message)) {
void window.postMessage(message);
}
}
if (
typeof browser !== "undefined" &&
typeof browser.runtime !== "undefined" &&
typeof browser.runtime.onMessage !== "undefined"
) {
browser.runtime.onMessage.addListener(listener);
return;
}
// eslint-disable-next-line no-restricted-syntax -- This doesn't run in the popup but in the content script
chrome.runtime.onMessage.addListener(listener);
}
setupMessageListener();

View File

@@ -0,0 +1,26 @@
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service";
import { IpcService } from "@bitwarden/common/platform/ipc";
import { IpcClient } from "@bitwarden/sdk-internal";
import { BackgroundCommunicationBackend } from "./background-communication-backend";
export class IpcBackgroundService extends IpcService {
private communicationProvider?: BackgroundCommunicationBackend;
constructor(private logService: LogService) {
super();
}
override async init() {
try {
// This function uses classes and functions defined in the SDK, so we need to wait for the SDK to load.
await SdkLoadService.Ready;
this.communicationProvider = new BackgroundCommunicationBackend();
await super.initWithClient(new IpcClient(this.communicationProvider));
} catch (e) {
this.logService.error("[IPC] Initialization failed", e);
}
}
}

View File

@@ -205,6 +205,7 @@ const mainConfig = {
"content/content-message-handler": "./src/autofill/content/content-message-handler.ts",
"content/fido2-content-script": "./src/autofill/fido2/content/fido2-content-script.ts",
"content/fido2-page-script": "./src/autofill/fido2/content/fido2-page-script.ts",
"content/ipc-content-script": "./src/platform/ipc/content/ipc-content-script.ts",
"notification/bar": "./src/autofill/notification/bar.ts",
"overlay/menu-button":
"./src/autofill/overlay/inline-menu/pages/button/bootstrap-autofill-inline-menu-button.ts",