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"
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"
18 CFTypeID SecACLGetTypeID();
19 CFTypeID SecTrustedApplicationGetTypeID();
20 Boolean _CFIsObjC(CFTypeID typeID, CFTypeRef obj);
29 bool g_override_am_i_bundled = false;
30 bool g_override_am_i_bundled_value = false;
32 bool UncachedAmIBundled() {
34 // All apps are bundled on iOS.
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"];
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.";
59 void SetOverrideAmIBundled(bool value) {
61 // It doesn't make sense not to be bundled on iOS.
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
81 return NSStringToFilePath(resourcePath);
84 OSType CreatorCodeForCFBundleRef(CFBundleRef bundle) {
85 OSType creator = kUnknownType;
86 CFBundleGetPackageInfo(bundle, NULL, &creator);
90 OSType CreatorCodeForApplication() {
91 CFBundleRef bundle = CFBundleGetMainBundle();
95 return CreatorCodeForCFBundleRef(bundle);
98 bool GetSearchPathDirectory(NSSearchPathDirectory directory,
99 NSSearchPathDomainMask domain_mask,
103 NSSearchPathForDirectoriesInDomains(directory, domain_mask, YES);
104 if ([dirs count] < 1) {
107 *result = NSStringToFilePath([dirs objectAtIndex:0]);
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";
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())
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] != '/')
158 // Go through the remaining components.
159 for (++it; it != components.end(); ++it) {
160 DCHECK_GT(it->length(), 0U);
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.
176 #define TYPE_NAME_FOR_CF_TYPE_DEFN(TypeCF) \
177 std::string TypeNameForCFType(TypeCF##Ref) { \
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);
206 void NSObjectRelease(void* obj) {
207 id<NSObject> nsobj = static_cast<id<NSObject> >(obj);
211 void* CFTypeRefToNSObjectAutorelease(CFTypeRef cf_object) {
212 // When GC is on, NSMakeCollectable marks cf_object for GC and autorelease
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.
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;
229 #if defined(GOOGLE_CHROME_BUILD)
230 return "com.google.Chrome";
232 return "org.chromium.Chromium";
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;
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)); \
250 const_cast<TypeNS*>(reinterpret_cast<const TypeNS*>(cf_val)); \
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)); \
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); \
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)); \
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);
295 CF_TO_NS_CAST_DEFN(CTFont, UIFont);
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) {
302 const_cast<NSFont*>(reinterpret_cast<const NSFont*>(cf_val));
304 CTFontGetTypeID() == CFGetTypeID(cf_val) ||
305 (_CFIsObjC(CTFontGetTypeID(), cf_val) &&
306 [ns_val isKindOfClass:NSClassFromString(@"NSFont")]));
310 CTFontRef NSToCFCast(NSFont* ns_val) {
311 CTFontRef cf_val = reinterpret_cast<CTFontRef>(ns_val);
313 CTFontGetTypeID() == CFGetTypeID(cf_val) ||
314 [ns_val isKindOfClass:NSClassFromString(@"NSFont")]);
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) { \
328 if (CFGetTypeID(cf_val) == TypeCF##GetTypeID()) { \
329 return (TypeCF##Ref)(cf_val); \
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); \
341 CF_CAST_DEFN(CFArray);
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);
350 CF_CAST_DEFN(CFString);
352 CF_CAST_DEFN(CFUUID);
354 CF_CAST_DEFN(CGColor);
359 CF_CAST_DEFN(CTFont);
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
365 CFCast<CTFontRef>(const CFTypeRef& cf_val) {
366 if (cf_val == NULL) {
369 if (CFGetTypeID(cf_val) == CTFontGetTypeID()) {
370 return (CTFontRef)(cf_val);
373 if (!_CFIsObjC(CTFontGetTypeID(), cf_val))
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);
384 CFCastStrict<CTFontRef>(const CFTypeRef& cf_val) {
385 CTFontRef rv = CFCast<CTFontRef>(cf_val);
386 DCHECK(cf_val == NULL || rv);
392 CF_CAST_DEFN(SecACL);
393 CF_CAST_DEFN(SecTrustedApplication);
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) +
407 base::SysCFStringRefToUTF8(actual_type_ref) +
411 NSString* FilePathToNSString(const FilePath& path) {
414 return [NSString stringWithUTF8String:path.value().c_str()];
417 FilePath NSStringToFilePath(NSString* str) {
420 return FilePath([str fileSystemRepresentation]);
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));
438 o << "Code: " << CFErrorGetCode(err)
439 << " Domain: " << CFErrorGetDomain(err)
440 << " Desc: " << desc.get();
442 o << "(" << errorDesc << ")";