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+
115use 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
918pub ( 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 ) ]
174201struct 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