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
+
1
15
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;
7
16
8
17
/// Deal with opening of browsers on Mac OS X using Core Foundation framework
9
18
pub ( super ) fn open_browser_internal (
@@ -19,21 +28,18 @@ pub(super) fn open_browser_internal(
19
28
Browser :: Safari => create_cf_url ( "file:///Applications/Safari.app/" ) ,
20
29
Browser :: Default => {
21
30
if let Some ( dummy_url) = create_cf_url ( "https://" ) {
22
- let mut err: CFErrorRef = std :: ptr :: null_mut ( ) ;
31
+ let mut err = MaybeUninit :: uninit ( ) ;
23
32
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 ( ) )
29
34
} ;
30
35
if result. is_null ( ) {
31
36
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 ( ) )
33
38
} ) ;
34
39
create_cf_url ( DEFAULT_BROWSER_URL )
35
40
} 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 ( ) ) } ;
37
43
log:: trace!( "default browser is {:?}" , & cf_url) ;
38
44
Some ( cf_url)
39
45
}
@@ -53,19 +59,27 @@ pub(super) fn open_browser_internal(
53
59
let cf_url = create_cf_url ( target. as_ref ( ) )
54
60
. ok_or_else ( || Error :: new ( ErrorKind :: Other , "failed to create CFURL" ) ) ?;
55
61
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 ( ) ;
58
72
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,
61
75
pass_thru_params : std:: ptr:: null ( ) ,
62
76
launch_flags : LS_LAUNCH_FLAG_DEFAULTS | LS_LAUNCH_FLAG_ASYNC ,
63
77
async_ref_con : std:: ptr:: null ( ) ,
64
78
} ;
65
79
66
80
// handle dry-run scenario
67
81
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 ) {
69
83
if path. is_dir ( ) {
70
84
log:: debug!( "dry-run: not actually opening the browser {}" , & browser) ;
71
85
Ok ( ( ) )
@@ -83,8 +97,7 @@ pub(super) fn open_browser_internal(
83
97
84
98
// launch the browser
85
99
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 ( ) ) } ;
88
101
log:: trace!( "received status: {}" , status) ;
89
102
if status == 0 {
90
103
Ok ( ( ) )
@@ -94,22 +107,36 @@ pub(super) fn open_browser_internal(
94
107
}
95
108
96
109
/// 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 > > {
98
111
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 ,
102
115
url_u8. as_ptr ( ) ,
103
116
url_u8. len ( ) as isize ,
104
- core_foundation :: string :: kCFStringEncodingUTF8 ,
105
- std :: ptr :: null ( ) ,
117
+ CFStringBuiltInEncodings :: EncodingUTF8 . 0 ,
118
+ None ,
106
119
)
107
- } ;
120
+ }
121
+ }
108
122
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) )
113
140
}
114
141
}
115
142
@@ -172,8 +199,8 @@ const LS_LAUNCH_FLAG_ASYNC: u32 = 0x00010000;
172
199
173
200
#[ repr( C ) ]
174
201
struct LSLaunchURLSpec {
175
- app_url : CFURLRef ,
176
- item_urls : CFArrayRef ,
202
+ app_url : * const CFURL ,
203
+ item_urls : * const CFArray ,
177
204
pass_thru_params : * const c_void ,
178
205
launch_flags : u32 ,
179
206
async_ref_con : * const c_void ,
@@ -185,16 +212,16 @@ extern "C" {
185
212
/// Used to get the default browser configured for the user. See:
186
213
/// https://developer.apple.com/documentation/coreservices/1448824-lscopydefaultapplicationurlforur?language=objc
187
214
fn LSCopyDefaultApplicationURLForURL (
188
- inURL : CFURLRef ,
215
+ inURL : & CFURL ,
189
216
inRoleMask : LSRolesMask ,
190
- outError : * mut CFErrorRef ,
191
- ) -> CFURLRef ;
217
+ outError : * mut * mut CFError ,
218
+ ) -> * mut CFURL ;
192
219
193
220
/// Used to launch the browser to open a url
194
221
/// https://developer.apple.com/documentation/coreservices/1441986-lsopenfromurlspec?language=objc
195
222
fn LSOpenFromURLSpec (
196
223
inLaunchSpec : * const LSLaunchURLSpec ,
197
- outLaunchedURL : * mut CFURLRef ,
224
+ outLaunchedURL : * mut * mut CFURL ,
198
225
) -> OSStatus ;
199
226
}
200
227
0 commit comments