1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-05 19:23:19 +00:00
Files
browser/libs/messaging/README.md
2025-09-12 15:28:28 -04:00

3.9 KiB

messaging

Owned by: platform

Services for sending and recieving messages from different contexts of the same application.

Usage

The best way to use messaging is along with a CommandDefinition to help encourage type safety on either side of the message. An overload of MessageSender.send exists that takes a string for the command and a Record<string, unknown> for the payload, this exists for backwards compatibility but all new uses are highly encouraged to use the CommandDefinition<T> overload.

import { MessageSender, MessageListener, CommandDefinition } from "@bitwarden/messaging";

const MY_MESSAGE = new CommandDefinition<{ data: number }>("myMessage");
const MY_EVENT = new CommandDefinition<{ name: string }>("myEvent");

class MyService {
  myEvent$: Observable<{ name: string }>;

  contructor(private messageSender: MessageSender, private messageListener: MessageListener) {
    this.myEvent$ = this.messageListener.messages$(MY_EVENT);
  }

  async doThing() {
    this.messageSender.send(MY_MESSAGE, { data: 5 });
  }
}

Implementation

MessageSender

MessageSender has 4 implementations to help sending messages to all the different contexts of a Bitwarden application.

SubjectMessageSender

The SubjectMessageSender takes an rxjs Subject and nexts the given payload along with a command property with the given command name.

MultiMessageSender

This implementation just takes an array of other MessageSender implementations and sends all the messages it gets to all of those implementations. This exists especially so we can use a SubjectMessageSender to handle messages from the same context and use another client specific implementation to handle messages with another context.

ChromeMessageSender

This implementation uses chrome.runtime.sendMessage to send messages to other contexts of our browser extension. This API is the only one of the bunch that is asynchronous under the hood but since the send method forces all MessageSenders to be sync. We take care of this by handling the promise through logging.

ElectronMainMessageSender

This implementation sends messages through the electron BrowserWindow.webContents.send API. This makes the message available through listeners added in the ipcRenderer.addListener API.

MessageListener

Observable

There is only one implementation implementation of MessageListener and it just takes an observable of all messages. Similar to how MultiMessageSender sends messages to multiple sources this listener can listen to multiple sources by using the RXJS operator merge to listen to multiple observable sources of messages. So far all of our message sources are able to be converted pretty easily to observables using some type of fromEventPattern function.

Subject

The same subject that is passed to SubjectMessageSender can also be converted to an observable using asObservable().

chrome.runtime.onMessage

For browser, We use the chrome.runtime.onMessage API and convert it to an observable using fromChromeEvent. Messages from this source are also tagged as being an external message (meaning not the same exact context) such that consumers could filter them out using filter(isExternalMessage). When messages are read from this API in Angular contexts we also use our runInsideAngular operator to force the messages into the Angular zone so that messages are able to affect the UI without every consumer doing that themselves.

IPC

Similar to the extensions implementation the browser renderer process wraps ipc.platform.onMessage to convert those messages into an observable.