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

96 lines
3.9 KiB
Markdown

# 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.
```typescript
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`][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 `MessageSender`s 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`][electron-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.
[chrome-runtime-sendMessage]: [https://developer.chrome.com/docs/extensions/reference/api/runtime#method-sendMessage]
[electron-send]: [https://www.electronjs.org/docs/latest/api/web-contents#contentssendchannel-args]