1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-14 15:23:33 +00:00

PM-19424: React to IPC disconnect (#14123)

* React to IPC disconnects

* Minor cleanup

* Update apps/desktop/package.json

Co-authored-by: Daniel García <dani-garcia@users.noreply.github.com>

* Relaxed ordering

---------

Co-authored-by: Daniel García <dani-garcia@users.noreply.github.com>
This commit is contained in:
Anders Åberg
2025-04-07 14:31:42 +02:00
committed by GitHub
parent aeb3b9f94b
commit 849aa546d4
3 changed files with 73 additions and 0 deletions

View File

@@ -49,6 +49,12 @@ trait Callback: Send + Sync {
fn error(&self, error: BitwardenError);
}
#[derive(uniffi::Enum, Debug)]
pub enum ConnectionStatus {
Connected,
Disconnected,
}
#[derive(uniffi::Object)]
pub struct MacOSProviderClient {
to_server_send: tokio::sync::mpsc::Sender<String>,
@@ -57,6 +63,9 @@ pub struct MacOSProviderClient {
response_callbacks_counter: AtomicU32,
#[allow(clippy::type_complexity)]
response_callbacks_queue: Arc<Mutex<HashMap<u32, (Box<dyn Callback>, Instant)>>>,
// Flag to track connection status - atomic for thread safety without locks
connection_status: Arc<std::sync::atomic::AtomicBool>,
}
#[uniffi::export]
@@ -74,11 +83,13 @@ impl MacOSProviderClient {
to_server_send,
response_callbacks_counter: AtomicU32::new(0),
response_callbacks_queue: Arc::new(Mutex::new(HashMap::new())),
connection_status: Arc::new(std::sync::atomic::AtomicBool::new(false)),
};
let path = desktop_core::ipc::path("autofill");
let queue = client.response_callbacks_queue.clone();
let connection_status = client.connection_status.clone();
std::thread::spawn(move || {
let rt = tokio::runtime::Builder::new_current_thread()
@@ -96,9 +107,11 @@ impl MacOSProviderClient {
match serde_json::from_str::<SerializedMessage>(&message) {
Ok(SerializedMessage::Command(CommandMessage::Connected)) => {
info!("Connected to server");
connection_status.store(true, std::sync::atomic::Ordering::Relaxed);
}
Ok(SerializedMessage::Command(CommandMessage::Disconnected)) => {
info!("Disconnected from server");
connection_status.store(false, std::sync::atomic::Ordering::Relaxed);
}
Ok(SerializedMessage::Message {
sequence_number,
@@ -159,6 +172,17 @@ impl MacOSProviderClient {
) {
self.send_message(request, Box::new(callback));
}
pub fn get_connection_status(&self) -> ConnectionStatus {
let is_connected = self
.connection_status
.load(std::sync::atomic::Ordering::Relaxed);
if is_connected {
ConnectionStatus::Connected
} else {
ConnectionStatus::Disconnected
}
}
}
#[derive(Serialize, Deserialize)]

View File

@@ -62,12 +62,56 @@ class CredentialProviderViewController: ASCredentialProviderViewController {
return MacOsProviderClient.connect()
}()
// Timer for checking connection status
private var connectionMonitorTimer: Timer?
private var lastConnectionStatus: ConnectionStatus = .disconnected
// Setup the connection monitoring timer
private func setupConnectionMonitoring() {
// Check connection status every 1 second
connectionMonitorTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
self?.checkConnectionStatus()
}
// Make sure timer runs even when UI is busy
RunLoop.current.add(connectionMonitorTimer!, forMode: .common)
// Initial check
checkConnectionStatus()
}
// Check the connection status by calling into Rust
private func checkConnectionStatus() {
// Get the current connection status from Rust
let currentStatus = client.getConnectionStatus()
// Only post notification if state changed
if currentStatus != lastConnectionStatus {
if(currentStatus == .connected) {
logger.log("[autofill-extension] Connection status changed: Connected")
} else {
logger.log("[autofill-extension] Connection status changed: Disconnected")
}
// Save the new status
lastConnectionStatus = currentStatus
// If we just disconnected, try to cancel the request
if currentStatus == .disconnected {
self.extensionContext.cancelRequest(withError: BitwardenError.Internal("Bitwarden desktop app disconnected"))
}
}
}
init() {
logger = Logger(subsystem: "com.bitwarden.desktop.autofill-extension", category: "credential-provider")
logger.log("[autofill-extension] initializing extension")
super.init(nibName: nil, bundle: nil)
// Setup connection monitoring now that self is available
setupConnectionMonitoring()
}
required init?(coder: NSCoder) {
@@ -76,6 +120,10 @@ class CredentialProviderViewController: ASCredentialProviderViewController {
deinit {
logger.log("[autofill-extension] deinitializing extension")
// Stop the connection monitor timer
connectionMonitorTimer?.invalidate()
connectionMonitorTimer = nil
}

View File

@@ -18,6 +18,7 @@
"scripts": {
"postinstall": "electron-rebuild",
"start": "cross-env ELECTRON_IS_DEV=0 ELECTRON_NO_UPDATER=1 electron ./build",
"build-native-macos": "cd desktop_native && ./macos_provider/build.sh && node build.js cross-platform",
"build-native": "cd desktop_native && node build.js",
"build": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" concurrently -n Main,Rend,Prel -c yellow,cyan \"npm run build:main\" \"npm run build:renderer\" \"npm run build:preload\"",
"build:dev": "concurrently -n Main,Rend -c yellow,cyan \"npm run build:main:dev\" \"npm run build:renderer:dev\"",