mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 00:03:56 +00:00
[PM-9035] desktop build logic to provide credentials to os on sync (#10181)
* feat: scaffold desktop_objc * feat: rename fido2 to autofill * feat: scaffold electron autofill * feat: auto call hello world on init * feat: scaffold call to basic objc function * feat: simple log that checks if autofill is enabled * feat: adding some availability guards * feat: scaffold services and allow calls from inspector * feat: create custom type for returning strings across rust/objc boundary * chore: clean up comments * feat: enable ARC * feat: add util function `c_string_to_nsstring` * chore: refactor and rename to `run_command` * feat: add try-catch around command execution * feat: properly implement command calling Add static typing. Add proper error handling. * feat: add autoreleasepool to avoid memory leaks * chore: change objc names to camelCase * fix: error returning * feat: extract some helper functions into utils class * feat: scaffold status command * feat: implement status command * feat: implement password credential mapping * wip: implement sync command This crashes because we are not properly handling the fact that `saveCredentialIdentities` uses callbacks, resulting in a race condition where we try to access a variable (result) that has already gotten dealloc'd. * feat: first version of callback * feat: make run_command async * feat: functioning callback returns * chore: refactor to make objc code easier to read and use * feat: refactor everything to use new callback return method * feat: re-implement status command with callback * fix: warning about CommandContext not being FFI-safe * feat: implement sync command using callbacks * feat: implement manual password credential sync * feat: add auto syncing * docs: add todo * feat: add support for passkeys * chore: move desktop autofill service to init service * feat: auto-add all .m files to builder * fix: native build on unix and windows * fix: unused compiler warnings * fix: napi type exports * feat: add corresponding dist command * feat: comment signing profile until we fix signing * fix: build breaking on non-macOS platforms * chore: cargo lock update * chore: revert accidental version change * feat: put sync behind feature flag * chore: put files in autofill folder * fix: obj-c code not recompiling on changes * feat: add `namespace` to commands * fix: linting complaining about flag * feat: add autofill as owner of their objc code * chore: make autofill owner of run_command in core crate * fix: re-add napi annotation * fix: remove dev bypass
This commit is contained in:
124
apps/desktop/desktop_native/objc/src/lib.rs
Normal file
124
apps/desktop/desktop_native/objc/src/lib.rs
Normal file
@@ -0,0 +1,124 @@
|
||||
#![cfg(target_os = "macos")]
|
||||
|
||||
use std::{
|
||||
ffi::{c_char, CStr, CString},
|
||||
os::raw::c_void,
|
||||
};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ObjCString {
|
||||
value: *const c_char,
|
||||
size: usize,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct CommandContext {
|
||||
tx: Option<tokio::sync::oneshot::Sender<String>>,
|
||||
}
|
||||
|
||||
impl CommandContext {
|
||||
pub fn new() -> (Self, tokio::sync::oneshot::Receiver<String>) {
|
||||
let (tx, rx) = tokio::sync::oneshot::channel::<String>();
|
||||
|
||||
(CommandContext { tx: Some(tx) }, rx)
|
||||
}
|
||||
|
||||
pub fn send(&mut self, value: String) -> Result<()> {
|
||||
let tx = self.tx.take().context(
|
||||
"Failed to take Sender from CommandContext. Has this context already returned once?",
|
||||
)?;
|
||||
|
||||
tx.send(value).map_err(|_| {
|
||||
anyhow::anyhow!("Failed to send ObjCString from CommandContext to Rust code")
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn as_ptr(&mut self) -> *mut c_void {
|
||||
self as *mut Self as *mut c_void
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<ObjCString> for String {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: ObjCString) -> Result<Self> {
|
||||
let c_str = unsafe { CStr::from_ptr(value.value) };
|
||||
let str = c_str
|
||||
.to_str()
|
||||
.context("Failed to convert ObjC output string to &str for use in Rust")?;
|
||||
|
||||
Ok(str.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ObjCString {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
objc::freeObjCString(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod objc {
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use super::*;
|
||||
|
||||
extern "C" {
|
||||
pub fn runCommand(context: *mut c_void, value: *const c_char);
|
||||
pub fn freeObjCString(value: &ObjCString);
|
||||
}
|
||||
|
||||
/// This function is called from the ObjC code to return the output of the command
|
||||
#[no_mangle]
|
||||
pub extern "C" fn commandReturn(context: &mut CommandContext, value: ObjCString) -> bool {
|
||||
let value: String = match value.try_into() {
|
||||
Ok(value) => value,
|
||||
Err(e) => {
|
||||
println!(
|
||||
"Error: Failed to convert ObjCString to Rust string during commandReturn: {}",
|
||||
e
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
match context.send(value) {
|
||||
Ok(_) => 0,
|
||||
Err(e) => {
|
||||
println!(
|
||||
"Error: Failed to return ObjCString from ObjC code to Rust code: {}",
|
||||
e
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run_command(input: String) -> Result<String> {
|
||||
// Convert input to type that can be passed to ObjC code
|
||||
let c_input = CString::new(input)
|
||||
.context("Failed to convert Rust input string to a CString for use in call to ObjC code")?;
|
||||
|
||||
let (mut context, rx) = CommandContext::new();
|
||||
|
||||
// Call ObjC code
|
||||
unsafe { objc::runCommand(context.as_ptr(), c_input.as_ptr()) };
|
||||
|
||||
// Convert output from ObjC code to Rust string
|
||||
let objc_output = rx.await?.try_into()?;
|
||||
|
||||
// Convert output from ObjC code to Rust string
|
||||
// let objc_output = output.try_into()?;
|
||||
|
||||
Ok(objc_output)
|
||||
}
|
||||
Reference in New Issue
Block a user