Blink roll 25b6bd3a7a131ffe68d809546ad1a20707915cdc:3a503f41ae42e5b79cfcd2ff10e65afde...
[chromium-blink-merge.git] / base / mac / foundation_util.mm
blob4e9b2248874c33959e40cceb0157099c6759b522
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.
5 #include "base/mac/foundation_util.h"
7 #include <stdlib.h>
8 #include <string.h>
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"
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
24 namespace base {
25 namespace mac {
27 namespace {
29 bool g_override_am_i_bundled = false;
30 bool g_override_am_i_bundled_value = false;
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;
40   // Yes, this is cheap.
41   return [[base::mac::OuterBundle() bundlePath] hasSuffix:@".app"];
42 #endif
45 }  // namespace
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;
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;
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;
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);
84 OSType CreatorCodeForCFBundleRef(CFBundleRef bundle) {
85   OSType creator = kUnknownType;
86   CFBundleGetPackageInfo(bundle, NULL, &creator);
87   return creator;
90 OSType CreatorCodeForApplication() {
91   CFBundleRef bundle = CFBundleGetMainBundle();
92   if (!bundle)
93     return kUnknownType;
95   return CreatorCodeForCFBundleRef(bundle);
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;
111 bool GetLocalDirectory(NSSearchPathDirectory directory, FilePath* result) {
112   return GetSearchPathDirectory(directory, NSLocalDomainMask, result);
115 bool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result) {
116   return GetSearchPathDirectory(directory, NSUserDomainMask, result);
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;
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;
136   // Split the path into components.
137   std::vector<std::string> components;
138   exec_name.GetComponents(&components);
140   // It's an error if we don't get any components.
141   if (!components.size())
142     return FilePath();
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);
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 += '/';
158   // Go through the remaining components.
159   for (++it; it != components.end(); ++it) {
160     DCHECK_GT(it->length(), 0U);
162     bundle_name += *it;
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);
169     // Separate this component from the next one.
170     bundle_name += '/';
171   }
173   return FilePath();
176 #define TYPE_NAME_FOR_CF_TYPE_DEFN(TypeCF) \
177 std::string TypeNameForCFType(TypeCF##Ref) { \
178   return #TypeCF; \
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);
194 TYPE_NAME_FOR_CF_TYPE_DEFN(CGColor);
196 TYPE_NAME_FOR_CF_TYPE_DEFN(CTFont);
197 TYPE_NAME_FOR_CF_TYPE_DEFN(CTRun);
199 #undef TYPE_NAME_FOR_CF_TYPE_DEFN
201 void NSObjectRetain(void* obj) {
202   id<NSObject> nsobj = static_cast<id<NSObject> >(obj);
203   [nsobj retain];
206 void NSObjectRelease(void* obj) {
207   id<NSObject> nsobj = static_cast<id<NSObject> >(obj);
208   [nsobj release];
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];
222 static const char* base_bundle_id;
224 const char* BaseBundleID() {
225   if (base_bundle_id) {
226     return base_bundle_id;
227   }
229 #if defined(GOOGLE_CHROME_BUILD)
230   return "com.google.Chrome";
231 #else
232   return "org.chromium.Chromium";
233 #endif
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   }
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) \
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 } \
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; \
260 #define CF_TO_NS_MUTABLE_CAST_DEFN(name) \
261 CF_TO_NS_CAST_DEFN(CF##name, NS##name) \
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 } \
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; \
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);
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;
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;
317 #endif
319 #undef CF_TO_NS_CAST_DEFN
320 #undef CF_TO_NS_MUTABLE_CAST_DEFN
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 } \
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; \
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);
354 CF_CAST_DEFN(CGColor);
356 CF_CAST_DEFN(CTRun);
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   }
373   if (!_CFIsObjC(CTFontGetTypeID(), cf_val))
374     return NULL;
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;
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;
389 #endif
391 #if !defined(OS_IOS)
392 CF_CAST_DEFN(SecACL);
393 CF_CAST_DEFN(SecTrustedApplication);
394 #endif
396 #undef CF_CAST_DEFN
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";
411 NSString* FilePathToNSString(const FilePath& path) {
412   if (path.empty())
413     return nil;
414   return [NSString stringWithUTF8String:path.value().c_str()];
417 FilePath NSStringToFilePath(NSString* str) {
418   if (![str length])
419     return FilePath();
420   return FilePath([str fileSystemRepresentation]);
423 }  // namespace mac
424 }  // namespace base
426 std::ostream& operator<<(std::ostream& o, const CFStringRef string) {
427   return o << base::SysCFStringRefToUTF8(string);
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;