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