Skip to main content

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}