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,23 +28,22 @@ 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
- if result. is_null ( ) {
31
- log:: error!( "failed to get default browser: {}" , unsafe {
32
- CFError :: wrap_under_create_rule( err)
33
- } ) ;
34
- create_cf_url ( DEFAULT_BROWSER_URL )
35
- } else {
36
- let cf_url = unsafe { CFURL :: wrap_under_create_rule ( result) } ;
35
+ if let Some ( result) = NonNull :: new ( result) {
36
+ let cf_url = unsafe { CFRetained :: from_raw ( result) } ;
37
37
log:: trace!( "default browser is {:?}" , & cf_url) ;
38
38
Some ( cf_url)
39
+ } else {
40
+ let error = unsafe {
41
+ CFRetained :: from_raw ( NonNull :: new ( err. assume_init ( ) ) . expect (
42
+ "Error should be set when LSCopyDefaultApplicationURLForURL() returns NULL" ,
43
+ ) )
44
+ } ;
45
+ log:: error!( "failed to get default browser: {}" , error) ;
46
+ create_cf_url ( DEFAULT_BROWSER_URL )
39
47
}
40
48
} else {
41
49
create_cf_url ( DEFAULT_BROWSER_URL )
@@ -53,19 +61,27 @@ pub(super) fn open_browser_internal(
53
61
let cf_url = create_cf_url ( target. as_ref ( ) )
54
62
. ok_or_else ( || Error :: new ( ErrorKind :: Other , "failed to create CFURL" ) ) ?;
55
63
56
- let urls_v = [ cf_url] ;
57
- let urls_arr = CFArray :: < CFURL > :: from_CFTypes ( & urls_v) ;
64
+ let mut urls_v = [ cf_url] ;
65
+ let urls_arr = unsafe {
66
+ CFArrayCreate (
67
+ None ,
68
+ urls_v. as_mut_ptr ( ) . cast ( ) ,
69
+ urls_v. len ( ) as isize ,
70
+ std:: ptr:: null ( ) ,
71
+ )
72
+ }
73
+ . expect ( "Failed to create CFArray from slice" ) ;
58
74
let spec = LSLaunchURLSpec {
59
- app_url : browser_cf_url. as_concrete_TypeRef ( ) ,
60
- item_urls : urls_arr. as_concrete_TypeRef ( ) ,
75
+ app_url : & * browser_cf_url,
76
+ item_urls : & * urls_arr,
61
77
pass_thru_params : std:: ptr:: null ( ) ,
62
78
launch_flags : LS_LAUNCH_FLAG_DEFAULTS | LS_LAUNCH_FLAG_ASYNC ,
63
79
async_ref_con : std:: ptr:: null ( ) ,
64
80
} ;
65
81
66
82
// handle dry-run scenario
67
83
if options. dry_run {
68
- return if let Some ( path) = browser_cf_url . to_path ( ) {
84
+ return if let Some ( path) = cf_url_as_path ( & browser_cf_url ) {
69
85
if path. is_dir ( ) {
70
86
log:: debug!( "dry-run: not actually opening the browser {}" , & browser) ;
71
87
Ok ( ( ) )
@@ -83,8 +99,7 @@ pub(super) fn open_browser_internal(
83
99
84
100
// launch the browser
85
101
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) } ;
102
+ let status = unsafe { LSOpenFromURLSpec ( & spec, std:: ptr:: null_mut ( ) ) } ;
88
103
log:: trace!( "received status: {}" , status) ;
89
104
if status == 0 {
90
105
Ok ( ( ) )
@@ -94,22 +109,37 @@ pub(super) fn open_browser_internal(
94
109
}
95
110
96
111
/// Create a Core Foundation CFURL object given a rust-y `url`
97
- fn create_cf_url ( url : & str ) -> Option < CFURL > {
112
+ fn create_cf_url ( url : & str ) -> Option < CFRetained < CFURL > > {
98
113
let url_u8 = url. as_bytes ( ) ;
99
- let url_ref = unsafe {
100
- core_foundation :: url :: CFURLCreateWithBytes (
101
- std :: ptr :: null ( ) ,
114
+ unsafe {
115
+ CFURLCreateWithBytes (
116
+ None ,
102
117
url_u8. as_ptr ( ) ,
103
118
url_u8. len ( ) as isize ,
104
- core_foundation :: string :: kCFStringEncodingUTF8 ,
105
- std :: ptr :: null ( ) ,
119
+ CFStringBuiltInEncodings :: EncodingUTF8 . 0 ,
120
+ None ,
106
121
)
107
- } ;
122
+ }
123
+ }
108
124
109
- if url_ref. is_null ( ) {
110
- None
111
- } else {
112
- Some ( unsafe { CFURL :: wrap_under_create_rule ( url_ref) } )
125
+ // Partially borrowed from https://docs.rs/core-foundation/0.10.0/src/core_foundation/url.rs.html#90-107
126
+ fn cf_url_as_path ( url : & CFURL ) -> Option < PathBuf > {
127
+ // From libc
128
+ pub const PATH_MAX : i32 = 1024 ;
129
+ // implementing this on Windows is more complicated because of the different OsStr representation
130
+ unsafe {
131
+ let mut buf = [ 0u8 ; PATH_MAX as usize ] ;
132
+ let result =
133
+ CFURLGetFileSystemRepresentation ( url, true , buf. as_mut_ptr ( ) , buf. len ( ) as isize ) ;
134
+ if !result {
135
+ return None ;
136
+ }
137
+ // let len = strlen(buf.as_ptr() as *const c_char);
138
+ // let path = OsStr::from_bytes(&buf[0..len]);
139
+ // TODO: Requires MSRV bump
140
+ let path = CStr :: from_bytes_until_nul ( & buf) . expect ( "buf must be NUL-terminated" ) ;
141
+ let path = OsStr :: from_bytes ( path. to_bytes ( ) ) ;
142
+ Some ( PathBuf :: from ( path) )
113
143
}
114
144
}
115
145
@@ -172,8 +202,8 @@ const LS_LAUNCH_FLAG_ASYNC: u32 = 0x00010000;
172
202
173
203
#[ repr( C ) ]
174
204
struct LSLaunchURLSpec {
175
- app_url : CFURLRef ,
176
- item_urls : CFArrayRef ,
205
+ app_url : * const CFURL ,
206
+ item_urls : * const CFArray ,
177
207
pass_thru_params : * const c_void ,
178
208
launch_flags : u32 ,
179
209
async_ref_con : * const c_void ,
@@ -185,16 +215,16 @@ extern "C" {
185
215
/// Used to get the default browser configured for the user. See:
186
216
/// https://developer.apple.com/documentation/coreservices/1448824-lscopydefaultapplicationurlforur?language=objc
187
217
fn LSCopyDefaultApplicationURLForURL (
188
- inURL : CFURLRef ,
218
+ inURL : & CFURL ,
189
219
inRoleMask : LSRolesMask ,
190
- outError : * mut CFErrorRef ,
191
- ) -> CFURLRef ;
220
+ outError : * mut * mut CFError ,
221
+ ) -> * mut CFURL ;
192
222
193
223
/// Used to launch the browser to open a url
194
224
/// https://developer.apple.com/documentation/coreservices/1441986-lsopenfromurlspec?language=objc
195
225
fn LSOpenFromURLSpec (
196
226
inLaunchSpec : * const LSLaunchURLSpec ,
197
- outLaunchedURL : * mut CFURLRef ,
227
+ outLaunchedURL : * mut * mut CFURL ,
198
228
) -> OSStatus ;
199
229
}
200
230
0 commit comments