1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/mac/foundation_util.h" 6 7 #include <stdlib.h> 8 #include <string.h> 9 10 #include "base/files/file_path.h" 11 #include "base/logging.h" 12 #include "base/mac/bundle_locations.h" 13 #include "base/mac/mac_logging.h" 14 #include "base/strings/sys_string_conversions.h" 15 16 #if !defined(OS_IOS) 17 extern "C" { 18 CFTypeID SecACLGetTypeID(); 19 CFTypeID SecTrustedApplicationGetTypeID(); 20 Boolean _CFIsObjC(CFTypeID typeID, CFTypeRef obj); 21 } // extern "C" 22 #endif 23 24 namespace base { 25 namespace mac { 26 27 namespace { 28 29 bool g_override_am_i_bundled = false; 30 bool g_override_am_i_bundled_value = false; 31 32 bool UncachedAmIBundled() { 33 #if defined(OS_IOS) 34 // All apps are bundled on iOS. 35 return true; 36 #else 37 if (g_override_am_i_bundled) 38 return g_override_am_i_bundled_value; 39 40 // Yes, this is cheap. 41 return [[base::mac::OuterBundle() bundlePath] hasSuffix:@".app"]; 42 #endif 43 } 44 45 } // namespace 46 47 bool AmIBundled() { 48 // If the return value is not cached, this function will return different 49 // values depending on when it's called. This confuses some client code, see 50 // http://crbug.com/63183 . 51 static bool result = UncachedAmIBundled(); 52 DCHECK_EQ(result, UncachedAmIBundled()) 53 << "The return value of AmIBundled() changed. This will confuse tests. " 54 << "Call SetAmIBundled() override manually if your test binary " 55 << "delay-loads the framework."; 56 return result; 57 } 58 59 void SetOverrideAmIBundled(bool value) { 60 #if defined(OS_IOS) 61 // It doesn't make sense not to be bundled on iOS. 62 if (!value) 63 NOTREACHED(); 64 #endif 65 g_override_am_i_bundled = true; 66 g_override_am_i_bundled_value = value; 67 } 68 69 bool IsBackgroundOnlyProcess() { 70 // This function really does want to examine NSBundle's idea of the main 71 // bundle dictionary. It needs to look at the actual running .app's 72 // Info.plist to access its LSUIElement property. 73 NSDictionary* info_dictionary = [base::mac::MainBundle() infoDictionary]; 74 return [[info_dictionary objectForKey:@"LSUIElement"] boolValue] != NO; 75 } 76 77 FilePath PathForFrameworkBundleResource(CFStringRef resourceName) { 78 NSBundle* bundle = base::mac::FrameworkBundle(); 79 NSString* resourcePath = [bundle pathForResource:(NSString*)resourceName 80 ofType:nil]; 81 return NSStringToFilePath(resourcePath); 82 } 83 84 OSType CreatorCodeForCFBundleRef(CFBundleRef bundle) { 85 OSType creator = kUnknownType; 86 CFBundleGetPackageInfo(bundle, NULL, &creator); 87 return creator; 88 } 89 90 OSType CreatorCodeForApplication() { 91 CFBundleRef bundle = CFBundleGetMainBundle(); 92 if (!bundle) 93 return kUnknownType; 94 95 return CreatorCodeForCFBundleRef(bundle); 96 } 97 98 bool GetSearchPathDirectory(NSSearchPathDirectory directory, 99 NSSearchPathDomainMask domain_mask, 100 FilePath* result) { 101 DCHECK(result); 102 NSArray* dirs = 103 NSSearchPathForDirectoriesInDomains(directory, domain_mask, YES); 104 if ([dirs count] < 1) { 105 return false; 106 } 107 *result = NSStringToFilePath([dirs objectAtIndex:0]); 108 return true; 109 } 110 111 bool GetLocalDirectory(NSSearchPathDirectory directory, FilePath* result) { 112 return GetSearchPathDirectory(directory, NSLocalDomainMask, result); 113 } 114 115 bool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result) { 116 return GetSearchPathDirectory(directory, NSUserDomainMask, result); 117 } 118 119 FilePath GetUserLibraryPath() { 120 FilePath user_library_path; 121 if (!GetUserDirectory(NSLibraryDirectory, &user_library_path)) { 122 DLOG(WARNING) << "Could not get user library path"; 123 } 124 return user_library_path; 125 } 126 127 // Takes a path to an (executable) binary and tries to provide the path to an 128 // application bundle containing it. It takes the outermost bundle that it can 129 // find (so for "/Foo/Bar.app/.../Baz.app/..." it produces "/Foo/Bar.app"). 130 // |exec_name| - path to the binary 131 // returns - path to the application bundle, or empty on error 132 FilePath GetAppBundlePath(const FilePath& exec_name) { 133 const char kExt[] = ".app"; 134 const size_t kExtLength = arraysize(kExt) - 1; 135 136 // Split the path into components. 137 std::vector<std::string> components; 138 exec_name.GetComponents(&components); 139 140 // It's an error if we don't get any components. 141 if (!components.size()) 142 return FilePath(); 143 144 // Don't prepend '/' to the first component. 145 std::vector<std::string>::const_iterator it = components.begin(); 146 std::string bundle_name = *it; 147 DCHECK_GT(it->length(), 0U); 148 // If the first component ends in ".app", we're already done. 149 if (it->length() > kExtLength && 150 !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength)) 151 return FilePath(bundle_name); 152 153 // The first component may be "/" or "//", etc. Only append '/' if it doesn't 154 // already end in '/'. 155 if (bundle_name[bundle_name.length() - 1] != '/') 156 bundle_name += '/'; 157 158 // Go through the remaining components. 159 for (++it; it != components.end(); ++it) { 160 DCHECK_GT(it->length(), 0U); 161 162 bundle_name += *it; 163 164 // If the current component ends in ".app", we're done. 165 if (it->length() > kExtLength && 166 !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength)) 167 return FilePath(bundle_name); 168 169 // Separate this component from the next one. 170 bundle_name += '/'; 171 } 172 173 return FilePath(); 174 } 175 176 #define TYPE_NAME_FOR_CF_TYPE_DEFN(TypeCF) \ 177 std::string TypeNameForCFType(TypeCF##Ref) { \ 178 return #TypeCF; \ 179 } 180 181 TYPE_NAME_FOR_CF_TYPE_DEFN(CFArray); 182 TYPE_NAME_FOR_CF_TYPE_DEFN(CFBag); 183 TYPE_NAME_FOR_CF_TYPE_DEFN(CFBoolean); 184 TYPE_NAME_FOR_CF_TYPE_DEFN(CFData); 185 TYPE_NAME_FOR_CF_TYPE_DEFN(CFDate); 186 TYPE_NAME_FOR_CF_TYPE_DEFN(CFDictionary); 187 TYPE_NAME_FOR_CF_TYPE_DEFN(CFNull); 188 TYPE_NAME_FOR_CF_TYPE_DEFN(CFNumber); 189 TYPE_NAME_FOR_CF_TYPE_DEFN(CFSet); 190 TYPE_NAME_FOR_CF_TYPE_DEFN(CFString); 191 TYPE_NAME_FOR_CF_TYPE_DEFN(CFURL); 192 TYPE_NAME_FOR_CF_TYPE_DEFN(CFUUID); 193 194 TYPE_NAME_FOR_CF_TYPE_DEFN(CGColor); 195 196 TYPE_NAME_FOR_CF_TYPE_DEFN(CTFont); 197 TYPE_NAME_FOR_CF_TYPE_DEFN(CTRun); 198 199 #undef TYPE_NAME_FOR_CF_TYPE_DEFN 200 201 void NSObjectRetain(void* obj) { 202 id<NSObject> nsobj = static_cast<id<NSObject> >(obj); 203 [nsobj retain]; 204 } 205 206 void NSObjectRelease(void* obj) { 207 id<NSObject> nsobj = static_cast<id<NSObject> >(obj); 208 [nsobj release]; 209 } 210 211 void* CFTypeRefToNSObjectAutorelease(CFTypeRef cf_object) { 212 // When GC is on, NSMakeCollectable marks cf_object for GC and autorelease 213 // is a no-op. 214 // 215 // In the traditional GC-less environment, NSMakeCollectable is a no-op, 216 // and cf_object is autoreleased, balancing out the caller's ownership claim. 217 // 218 // NSMakeCollectable returns nil when used on a NULL object. 219 return [NSMakeCollectable(cf_object) autorelease]; 220 } 221 222 static const char* base_bundle_id; 223 224 const char* BaseBundleID() { 225 if (base_bundle_id) { 226 return base_bundle_id; 227 } 228 229 #if defined(GOOGLE_CHROME_BUILD) 230 return "com.google.Chrome"; 231 #else 232 return "org.chromium.Chromium"; 233 #endif 234 } 235 236 void SetBaseBundleID(const char* new_base_bundle_id) { 237 if (new_base_bundle_id != base_bundle_id) { 238 free((void*)base_bundle_id); 239 base_bundle_id = new_base_bundle_id ? strdup(new_base_bundle_id) : NULL; 240 } 241 } 242 243 // Definitions for the corresponding CF_TO_NS_CAST_DECL macros in 244 // foundation_util.h. 245 #define CF_TO_NS_CAST_DEFN(TypeCF, TypeNS) \ 246 \ 247 TypeNS* CFToNSCast(TypeCF##Ref cf_val) { \ 248 DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val)); \ 249 TypeNS* ns_val = \ 250 const_cast<TypeNS*>(reinterpret_cast<const TypeNS*>(cf_val)); \ 251 return ns_val; \ 252 } \ 253 \ 254 TypeCF##Ref NSToCFCast(TypeNS* ns_val) { \ 255 TypeCF##Ref cf_val = reinterpret_cast<TypeCF##Ref>(ns_val); \ 256 DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val)); \ 257 return cf_val; \ 258 } 259 260 #define CF_TO_NS_MUTABLE_CAST_DEFN(name) \ 261 CF_TO_NS_CAST_DEFN(CF##name, NS##name) \ 262 \ 263 NSMutable##name* CFToNSCast(CFMutable##name##Ref cf_val) { \ 264 DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val)); \ 265 NSMutable##name* ns_val = reinterpret_cast<NSMutable##name*>(cf_val); \ 266 return ns_val; \ 267 } \ 268 \ 269 CFMutable##name##Ref NSToCFCast(NSMutable##name* ns_val) { \ 270 CFMutable##name##Ref cf_val = \ 271 reinterpret_cast<CFMutable##name##Ref>(ns_val); \ 272 DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val)); \ 273 return cf_val; \ 274 } 275 276 CF_TO_NS_MUTABLE_CAST_DEFN(Array); 277 CF_TO_NS_MUTABLE_CAST_DEFN(AttributedString); 278 CF_TO_NS_CAST_DEFN(CFCalendar, NSCalendar); 279 CF_TO_NS_MUTABLE_CAST_DEFN(CharacterSet); 280 CF_TO_NS_MUTABLE_CAST_DEFN(Data); 281 CF_TO_NS_CAST_DEFN(CFDate, NSDate); 282 CF_TO_NS_MUTABLE_CAST_DEFN(Dictionary); 283 CF_TO_NS_CAST_DEFN(CFError, NSError); 284 CF_TO_NS_CAST_DEFN(CFLocale, NSLocale); 285 CF_TO_NS_CAST_DEFN(CFNumber, NSNumber); 286 CF_TO_NS_CAST_DEFN(CFRunLoopTimer, NSTimer); 287 CF_TO_NS_CAST_DEFN(CFTimeZone, NSTimeZone); 288 CF_TO_NS_MUTABLE_CAST_DEFN(Set); 289 CF_TO_NS_CAST_DEFN(CFReadStream, NSInputStream); 290 CF_TO_NS_CAST_DEFN(CFWriteStream, NSOutputStream); 291 CF_TO_NS_MUTABLE_CAST_DEFN(String); 292 CF_TO_NS_CAST_DEFN(CFURL, NSURL); 293 294 #if defined(OS_IOS) 295 CF_TO_NS_CAST_DEFN(CTFont, UIFont); 296 #else 297 // The NSFont/CTFont toll-free bridging is broken when it comes to type 298 // checking, so do some special-casing. 299 // http://www.openradar.me/15341349 rdar://15341349 300 NSFont* CFToNSCast(CTFontRef cf_val) { 301 NSFont* ns_val = 302 const_cast<NSFont*>(reinterpret_cast<const NSFont*>(cf_val)); 303 DCHECK(!cf_val || 304 CTFontGetTypeID() == CFGetTypeID(cf_val) || 305 (_CFIsObjC(CTFontGetTypeID(), cf_val) && 306 [ns_val isKindOfClass:NSClassFromString(@"NSFont")])); 307 return ns_val; 308 } 309 310 CTFontRef NSToCFCast(NSFont* ns_val) { 311 CTFontRef cf_val = reinterpret_cast<CTFontRef>(ns_val); 312 DCHECK(!cf_val || 313 CTFontGetTypeID() == CFGetTypeID(cf_val) || 314 [ns_val isKindOfClass:NSClassFromString(@"NSFont")]); 315 return cf_val; 316 } 317 #endif 318 319 #undef CF_TO_NS_CAST_DEFN 320 #undef CF_TO_NS_MUTABLE_CAST_DEFN 321 322 #define CF_CAST_DEFN(TypeCF) \ 323 template<> TypeCF##Ref \ 324 CFCast<TypeCF##Ref>(const CFTypeRef& cf_val) { \ 325 if (cf_val == NULL) { \ 326 return NULL; \ 327 } \ 328 if (CFGetTypeID(cf_val) == TypeCF##GetTypeID()) { \ 329 return (TypeCF##Ref)(cf_val); \ 330 } \ 331 return NULL; \ 332 } \ 333 \ 334 template<> TypeCF##Ref \ 335 CFCastStrict<TypeCF##Ref>(const CFTypeRef& cf_val) { \ 336 TypeCF##Ref rv = CFCast<TypeCF##Ref>(cf_val); \ 337 DCHECK(cf_val == NULL || rv); \ 338 return rv; \ 339 } 340 341 CF_CAST_DEFN(CFArray); 342 CF_CAST_DEFN(CFBag); 343 CF_CAST_DEFN(CFBoolean); 344 CF_CAST_DEFN(CFData); 345 CF_CAST_DEFN(CFDate); 346 CF_CAST_DEFN(CFDictionary); 347 CF_CAST_DEFN(CFNull); 348 CF_CAST_DEFN(CFNumber); 349 CF_CAST_DEFN(CFSet); 350 CF_CAST_DEFN(CFString); 351 CF_CAST_DEFN(CFURL); 352 CF_CAST_DEFN(CFUUID); 353 354 CF_CAST_DEFN(CGColor); 355 356 CF_CAST_DEFN(CTRun); 357 358 #if defined(OS_IOS) 359 CF_CAST_DEFN(CTFont); 360 #else 361 // The NSFont/CTFont toll-free bridging is broken when it comes to type 362 // checking, so do some special-casing. 363 // http://www.openradar.me/15341349 rdar://15341349 364 template<> CTFontRef 365 CFCast<CTFontRef>(const CFTypeRef& cf_val) { 366 if (cf_val == NULL) { 367 return NULL; 368 } 369 if (CFGetTypeID(cf_val) == CTFontGetTypeID()) { 370 return (CTFontRef)(cf_val); 371 } 372 373 if (!_CFIsObjC(CTFontGetTypeID(), cf_val)) 374 return NULL; 375 376 id<NSObject> ns_val = reinterpret_cast<id>(const_cast<void*>(cf_val)); 377 if ([ns_val isKindOfClass:NSClassFromString(@"NSFont")]) { 378 return (CTFontRef)(cf_val); 379 } 380 return NULL; 381 } 382 383 template<> CTFontRef 384 CFCastStrict<CTFontRef>(const CFTypeRef& cf_val) { 385 CTFontRef rv = CFCast<CTFontRef>(cf_val); 386 DCHECK(cf_val == NULL || rv); 387 return rv; 388 } 389 #endif 390 391 #if !defined(OS_IOS) 392 CF_CAST_DEFN(SecACL); 393 CF_CAST_DEFN(SecTrustedApplication); 394 #endif 395 396 #undef CF_CAST_DEFN 397 398 std::string GetValueFromDictionaryErrorMessage( 399 CFStringRef key, const std::string& expected_type, CFTypeRef value) { 400 ScopedCFTypeRef<CFStringRef> actual_type_ref( 401 CFCopyTypeIDDescription(CFGetTypeID(value))); 402 return "Expected value for key " + 403 base::SysCFStringRefToUTF8(key) + 404 " to be " + 405 expected_type + 406 " but it was " + 407 base::SysCFStringRefToUTF8(actual_type_ref) + 408 " instead"; 409 } 410 411 NSString* FilePathToNSString(const FilePath& path) { 412 if (path.empty()) 413 return nil; 414 return [NSString stringWithUTF8String:path.value().c_str()]; 415 } 416 417 FilePath NSStringToFilePath(NSString* str) { 418 if (![str length]) 419 return FilePath(); 420 return FilePath([str fileSystemRepresentation]); 421 } 422 423 } // namespace mac 424 } // namespace base 425 426 std::ostream& operator<<(std::ostream& o, const CFStringRef string) { 427 return o << base::SysCFStringRefToUTF8(string); 428 } 429 430 std::ostream& operator<<(std::ostream& o, const CFErrorRef err) { 431 base::ScopedCFTypeRef<CFStringRef> desc(CFErrorCopyDescription(err)); 432 base::ScopedCFTypeRef<CFDictionaryRef> user_info(CFErrorCopyUserInfo(err)); 433 CFStringRef errorDesc = NULL; 434 if (user_info.get()) { 435 errorDesc = reinterpret_cast<CFStringRef>( 436 CFDictionaryGetValue(user_info.get(), kCFErrorDescriptionKey)); 437 } 438 o << "Code: " << CFErrorGetCode(err) 439 << " Domain: " << CFErrorGetDomain(err) 440 << " Desc: " << desc.get(); 441 if(errorDesc) { 442 o << "(" << errorDesc << ")"; 443 } 444 return o; 445 } 446