foundation_util.mm 16 KB

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