Skip to content

Commit 8954442

Browse files
committed
macos: Migrate to objc2-core-foundation
1 parent 3ea6f97 commit 8954442

File tree

2 files changed

+72
-38
lines changed

2 files changed

+72
-38
lines changed

Cargo.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,14 @@ wasm-console = ["web-sys/console"]
2929
home = "0.5"
3030

3131
[target.'cfg(target_os = "macos")'.dependencies]
32-
core-foundation = "0.10"
32+
objc2-core-foundation = { version = "0.3", default-features = false, features = [
33+
"std",
34+
"CFArray",
35+
"CFBase",
36+
"CFError",
37+
"CFString",
38+
"CFURL",
39+
] }
3340

3441
[target.'cfg(target_os = "android")'.dependencies]
3542
jni = "0.21"

src/macos.rs

Lines changed: 64 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
1+
use std::{
2+
ffi::c_void,
3+
ffi::{CStr, OsStr},
4+
mem::MaybeUninit,
5+
os::unix::ffi::OsStrExt,
6+
path::PathBuf,
7+
ptr::NonNull,
8+
};
9+
10+
use objc2_core_foundation::{
11+
CFArray, CFArrayCreate, CFError, CFRetained, CFStringBuiltInEncodings, CFURLCreateWithBytes,
12+
CFURLGetFileSystemRepresentation, CFURL,
13+
};
14+
115
use crate::{Browser, BrowserOptions, Error, ErrorKind, Result, TargetType};
2-
use core_foundation::array::{CFArray, CFArrayRef};
3-
use core_foundation::base::TCFType;
4-
use core_foundation::error::{CFError, CFErrorRef};
5-
use core_foundation::url::{CFURLRef, CFURL};
6-
use std::os::raw::c_void;
716

817
/// Deal with opening of browsers on Mac OS X using Core Foundation framework
918
pub(super) fn open_browser_internal(
@@ -19,21 +28,18 @@ pub(super) fn open_browser_internal(
1928
Browser::Safari => create_cf_url("file:///Applications/Safari.app/"),
2029
Browser::Default => {
2130
if let Some(dummy_url) = create_cf_url("https://") {
22-
let mut err: CFErrorRef = std::ptr::null_mut();
31+
let mut err = MaybeUninit::uninit();
2332
let result = unsafe {
24-
LSCopyDefaultApplicationURLForURL(
25-
dummy_url.as_concrete_TypeRef(),
26-
LSROLE_VIEWER,
27-
&mut err,
28-
)
33+
LSCopyDefaultApplicationURLForURL(&*dummy_url, LSROLE_VIEWER, err.as_mut_ptr())
2934
};
3035
if result.is_null() {
3136
log::error!("failed to get default browser: {}", unsafe {
32-
CFError::wrap_under_create_rule(err)
37+
CFRetained::<CFError>::from_raw(NonNull::new(err.assume_init()).unwrap())
3338
});
3439
create_cf_url(DEFAULT_BROWSER_URL)
3540
} else {
36-
let cf_url = unsafe { CFURL::wrap_under_create_rule(result) };
41+
let cf_url =
42+
unsafe { CFRetained::<CFURL>::from_raw(NonNull::new(result).unwrap()) };
3743
log::trace!("default browser is {:?}", &cf_url);
3844
Some(cf_url)
3945
}
@@ -53,19 +59,27 @@ pub(super) fn open_browser_internal(
5359
let cf_url = create_cf_url(target.as_ref())
5460
.ok_or_else(|| Error::new(ErrorKind::Other, "failed to create CFURL"))?;
5561

56-
let urls_v = [cf_url];
57-
let urls_arr = CFArray::<CFURL>::from_CFTypes(&urls_v);
62+
let mut urls_v = [cf_url];
63+
let urls_arr = unsafe {
64+
CFArrayCreate(
65+
None,
66+
urls_v.as_mut_ptr().cast(),
67+
urls_v.len() as isize,
68+
std::ptr::null(),
69+
)
70+
}
71+
.unwrap();
5872
let spec = LSLaunchURLSpec {
59-
app_url: browser_cf_url.as_concrete_TypeRef(),
60-
item_urls: urls_arr.as_concrete_TypeRef(),
73+
app_url: &*browser_cf_url,
74+
item_urls: &*urls_arr,
6175
pass_thru_params: std::ptr::null(),
6276
launch_flags: LS_LAUNCH_FLAG_DEFAULTS | LS_LAUNCH_FLAG_ASYNC,
6377
async_ref_con: std::ptr::null(),
6478
};
6579

6680
// handle dry-run scenario
6781
if options.dry_run {
68-
return if let Some(path) = browser_cf_url.to_path() {
82+
return if let Some(path) = cf_url_as_path(&browser_cf_url) {
6983
if path.is_dir() {
7084
log::debug!("dry-run: not actually opening the browser {}", &browser);
7185
Ok(())
@@ -83,8 +97,7 @@ pub(super) fn open_browser_internal(
8397

8498
// launch the browser
8599
log::trace!("about to start browser: {} for {}", &browser, &target);
86-
let mut launched_app: CFURLRef = std::ptr::null_mut();
87-
let status = unsafe { LSOpenFromURLSpec(&spec, &mut launched_app) };
100+
let status = unsafe { LSOpenFromURLSpec(&spec, std::ptr::null_mut()) };
88101
log::trace!("received status: {}", status);
89102
if status == 0 {
90103
Ok(())
@@ -94,22 +107,36 @@ pub(super) fn open_browser_internal(
94107
}
95108

96109
/// Create a Core Foundation CFURL object given a rust-y `url`
97-
fn create_cf_url(url: &str) -> Option<CFURL> {
110+
fn create_cf_url(url: &str) -> Option<CFRetained<CFURL>> {
98111
let url_u8 = url.as_bytes();
99-
let url_ref = unsafe {
100-
core_foundation::url::CFURLCreateWithBytes(
101-
std::ptr::null(),
112+
unsafe {
113+
CFURLCreateWithBytes(
114+
None,
102115
url_u8.as_ptr(),
103116
url_u8.len() as isize,
104-
core_foundation::string::kCFStringEncodingUTF8,
105-
std::ptr::null(),
117+
CFStringBuiltInEncodings::EncodingUTF8.0,
118+
None,
106119
)
107-
};
120+
}
121+
}
108122

109-
if url_ref.is_null() {
110-
None
111-
} else {
112-
Some(unsafe { CFURL::wrap_under_create_rule(url_ref) })
123+
// Partially borrowed from https://docs.rs/core-foundation/0.10.0/src/core_foundation/url.rs.html#90-107
124+
fn cf_url_as_path(url: &CFURL) -> Option<PathBuf> {
125+
// From libc
126+
pub const PATH_MAX: i32 = 1024;
127+
// implementing this on Windows is more complicated because of the different OsStr representation
128+
unsafe {
129+
let mut buf = [0u8; PATH_MAX as usize];
130+
let result =
131+
CFURLGetFileSystemRepresentation(url, true, buf.as_mut_ptr(), buf.len() as isize);
132+
if !result {
133+
return None;
134+
}
135+
// let len = strlen(buf.as_ptr() as *const c_char);
136+
// let path = OsStr::from_bytes(&buf[0..len]);
137+
let path = CStr::from_bytes_until_nul(&buf).unwrap();
138+
let path = OsStr::from_bytes(path.to_bytes());
139+
Some(PathBuf::from(path))
113140
}
114141
}
115142

@@ -172,8 +199,8 @@ const LS_LAUNCH_FLAG_ASYNC: u32 = 0x00010000;
172199

173200
#[repr(C)]
174201
struct LSLaunchURLSpec {
175-
app_url: CFURLRef,
176-
item_urls: CFArrayRef,
202+
app_url: *const CFURL,
203+
item_urls: *const CFArray,
177204
pass_thru_params: *const c_void,
178205
launch_flags: u32,
179206
async_ref_con: *const c_void,
@@ -185,16 +212,16 @@ extern "C" {
185212
/// Used to get the default browser configured for the user. See:
186213
/// https://developer.apple.com/documentation/coreservices/1448824-lscopydefaultapplicationurlforur?language=objc
187214
fn LSCopyDefaultApplicationURLForURL(
188-
inURL: CFURLRef,
215+
inURL: &CFURL,
189216
inRoleMask: LSRolesMask,
190-
outError: *mut CFErrorRef,
191-
) -> CFURLRef;
217+
outError: *mut *mut CFError,
218+
) -> *mut CFURL;
192219

193220
/// Used to launch the browser to open a url
194221
/// https://developer.apple.com/documentation/coreservices/1441986-lsopenfromurlspec?language=objc
195222
fn LSOpenFromURLSpec(
196223
inLaunchSpec: *const LSLaunchURLSpec,
197-
outLaunchedURL: *mut CFURLRef,
224+
outLaunchedURL: *mut *mut CFURL,
198225
) -> OSStatus;
199226
}
200227

0 commit comments

Comments
 (0)