bitwarden_ipc/broadcast.rs
1use serde::{Deserialize, Serialize};
2
3use crate::{
4 endpoint::{Endpoint, HostId},
5 error::SendError,
6};
7
8/// A group of endpoints that a message sent with
9/// [`IpcClient::broadcast`](crate::IpcClient::broadcast) is delivered to.
10///
11/// The platform resolves a group into its current members at send time (see
12/// [`CommunicationBackend::resolve`](crate::traits::CommunicationBackend::resolve)). Each member
13/// receives its own encrypted message over its own session, so a broadcast never weakens the
14/// per-peer guarantees of a unicast send.
15#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
16#[bitwarden_ffi::wasm_record]
17pub enum BroadcastGroup {
18 /// Every [`BrowserForeground`](Endpoint::BrowserForeground) endpoint (popup, sidebar, or
19 /// extension page) that the platform currently knows about.
20 AllBrowserForeground,
21 /// Every [`BrowserBackground`](Endpoint::BrowserBackground) endpoint addressed by a specific
22 /// [`HostId::Id`] that the platform currently knows about (e.g. every
23 /// browser extension connected to the desktop app over native messaging).
24 AllBrowserBackground,
25}
26
27impl BroadcastGroup {
28 /// Whether `endpoint` can be a member of this group. The IPC client uses this to reject a
29 /// platform resolution that names an endpoint outside the group.
30 pub(crate) fn contains(&self, endpoint: &Endpoint) -> bool {
31 match self {
32 BroadcastGroup::AllBrowserForeground => {
33 matches!(endpoint, Endpoint::BrowserForeground { .. })
34 }
35 // `HostId::Own` is relational and only meaningful to the sender's own background, so
36 // it never names a distinct member of a group.
37 BroadcastGroup::AllBrowserBackground => {
38 matches!(endpoint, Endpoint::BrowserBackground { id: HostId::Id(_) })
39 }
40 }
41 }
42}
43
44/// The outcome of delivering a broadcast message to one member of a [`BroadcastGroup`].
45#[derive(Debug, Clone, PartialEq, Eq)]
46pub struct EndpointSendResult {
47 /// The endpoint the message was sent to.
48 pub endpoint: Endpoint,
49 /// The result of the send to this endpoint. [`SendError::Unreachable`] is an expected outcome
50 /// for a broadcast, e.g. when a popup closed between resolution and delivery.
51 pub result: Result<(), SendError>,
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57
58 #[test]
59 fn all_browser_foreground_contains_only_browser_foreground_endpoints() {
60 let group = BroadcastGroup::AllBrowserForeground;
61
62 assert!(group.contains(&Endpoint::BrowserForeground { id: 1 }));
63 assert!(!group.contains(&Endpoint::BrowserBackground { id: HostId::Own }));
64 assert!(!group.contains(&Endpoint::Web {
65 tab_id: 1,
66 document_id: "doc".to_string(),
67 }));
68 assert!(!group.contains(&Endpoint::DesktopMain));
69 }
70
71 #[test]
72 fn all_browser_background_contains_only_specific_browser_background_endpoints() {
73 let group = BroadcastGroup::AllBrowserBackground;
74
75 assert!(group.contains(&Endpoint::BrowserBackground { id: HostId::Id(7) }));
76 assert!(!group.contains(&Endpoint::BrowserBackground { id: HostId::Own }));
77 assert!(!group.contains(&Endpoint::BrowserForeground { id: 1 }));
78 assert!(!group.contains(&Endpoint::DesktopMain));
79 }
80}