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();
26 static bool g_override_am_i_bundled = false;
27 static bool g_override_am_i_bundled_value = false;
29 // Adapted from http://developer.apple.com/carbon/tipsandtricks.html#AmIBundled
30 static bool UncachedAmIBundled() {
32 // All apps are bundled on iOS
35 if (g_override_am_i_bundled)
36 return g_override_am_i_bundled_value;
38 ProcessSerialNumber psn = {0, kCurrentProcess};
42 if ((pbErr = GetProcessBundleLocation(&psn, &fsref)) != noErr) {
43 OSSTATUS_DLOG(ERROR, pbErr) << "GetProcessBundleLocation failed";
49 if ((fsErr = FSGetCatalogInfo(&fsref, kFSCatInfoNodeFlags, &info,
50 NULL, NULL, NULL)) != noErr) {
51 OSSTATUS_DLOG(ERROR, fsErr) << "FSGetCatalogInfo failed";
55 return info.nodeFlags & kFSNodeIsDirectoryMask;
60 // If the return value is not cached, this function will return different
61 // values depending on when it's called. This confuses some client code, see
62 // http://crbug.com/63183 .
63 static bool result = UncachedAmIBundled();
64 DCHECK_EQ(result, UncachedAmIBundled())
65 << "The return value of AmIBundled() changed. This will confuse tests. "
66 << "Call SetAmIBundled() override manually if your test binary "
67 << "delay-loads the framework.";
71 void SetOverrideAmIBundled(bool value) {
73 // It doesn't make sense not to be bundled on iOS.
77 g_override_am_i_bundled = true;
78 g_override_am_i_bundled_value = value;
81 bool IsBackgroundOnlyProcess() {
82 // This function really does want to examine NSBundle's idea of the main
83 // bundle dictionary. It needs to look at the actual running .app's
84 // Info.plist to access its LSUIElement property.
85 NSDictionary* info_dictionary = [base::mac::MainBundle() infoDictionary];
86 return [[info_dictionary objectForKey:@"LSUIElement"] boolValue] != NO;
89 FilePath PathForFrameworkBundleResource(CFStringRef resourceName) {
90 NSBundle* bundle = base::mac::FrameworkBundle();
91 NSString* resourcePath = [bundle pathForResource:(NSString*)resourceName
93 return NSStringToFilePath(resourcePath);
96 OSType CreatorCodeForCFBundleRef(CFBundleRef bundle) {
97 OSType creator = kUnknownType;
98 CFBundleGetPackageInfo(bundle, NULL, &creator);
102 OSType CreatorCodeForApplication() {
103 CFBundleRef bundle = CFBundleGetMainBundle();
107 return CreatorCodeForCFBundleRef(bundle);
110 bool GetSearchPathDirectory(NSSearchPathDirectory directory,
111 NSSearchPathDomainMask domain_mask,
115 NSSearchPathForDirectoriesInDomains(directory, domain_mask, YES);
116 if ([dirs count] < 1) {
119 *result = NSStringToFilePath([dirs objectAtIndex:0]);
123 bool GetLocalDirectory(NSSearchPathDirectory directory, FilePath* result) {
124 return GetSearchPathDirectory(directory, NSLocalDomainMask, result);
127 bool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result) {
128 return GetSearchPathDirectory(directory, NSUserDomainMask, result);
131 FilePath GetUserLibraryPath() {
132 FilePath user_library_path;
133 if (!GetUserDirectory(NSLibraryDirectory, &user_library_path)) {
134 DLOG(WARNING) << "Could not get user library path";
136 return user_library_path;
139 // Takes a path to an (executable) binary and tries to provide the path to an
140 // application bundle containing it. It takes the outermost bundle that it can
141 // find (so for "/Foo/Bar.app/.../Baz.app/..." it produces "/Foo/Bar.app").
142 // |exec_name| - path to the binary
143 // returns - path to the application bundle, or empty on error
144 FilePath GetAppBundlePath(const FilePath& exec_name) {
145 const char kExt[] = ".app";
146 const size_t kExtLength = arraysize(kExt) - 1;
148 // Split the path into components.
149 std::vector<std::string> components;
150 exec_name.GetComponents(&components);
152 // It's an error if we don't get any components.
153 if (!components.size())
156 // Don't prepend '/' to the first component.
157 std::vector<std::string>::const_iterator it = components.begin();
158 std::string bundle_name = *it;
159 DCHECK_GT(it->length(), 0U);
160 // If the first component ends in ".app", we're already done.
161 if (it->length() > kExtLength &&
162 !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength))
163 return FilePath(bundle_name);
165 // The first component may be "/" or "//", etc. Only append '/' if it doesn't
166 // already end in '/'.
167 if (bundle_name[bundle_name.length() - 1] != '/')
170 // Go through the remaining components.
171 for (++it; it != components.end(); ++it) {
172 DCHECK_GT(it->length(), 0U);
176 // If the current component ends in ".app", we're done.
177 if (it->length() > kExtLength &&
178 !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength))
179 return FilePath(bundle_name);
181 // Separate this component from the next one.
188 #define TYPE_NAME_FOR_CF_TYPE_DEFN(TypeCF) \
189 std::string TypeNameForCFType(TypeCF##Ref) { \
193 TYPE_NAME_FOR_CF_TYPE_DEFN(CFArray);
194 TYPE_NAME_FOR_CF_TYPE_DEFN(CFBag);
195 TYPE_NAME_FOR_CF_TYPE_DEFN(CFBoolean);
196 TYPE_NAME_FOR_CF_TYPE_DEFN(CFData);
197 TYPE_NAME_FOR_CF_TYPE_DEFN(CFDate);
198 TYPE_NAME_FOR_CF_TYPE_DEFN(CFDictionary);
199 TYPE_NAME_FOR_CF_TYPE_DEFN(CFNull);
200 TYPE_NAME_FOR_CF_TYPE_DEFN(CFNumber);
201 TYPE_NAME_FOR_CF_TYPE_DEFN(CFSet);
202 TYPE_NAME_FOR_CF_TYPE_DEFN(CFString);
203 TYPE_NAME_FOR_CF_TYPE_DEFN(CFURL);
204 TYPE_NAME_FOR_CF_TYPE_DEFN(CFUUID);
206 TYPE_NAME_FOR_CF_TYPE_DEFN(CGColor);
208 TYPE_NAME_FOR_CF_TYPE_DEFN(CTFont);
209 TYPE_NAME_FOR_CF_TYPE_DEFN(CTRun);
211 #undef TYPE_NAME_FOR_CF_TYPE_DEFN
213 void NSObjectRetain(void* obj) {
214 id<NSObject> nsobj = static_cast<id<NSObject> >(obj);
218 void NSObjectRelease(void* obj) {
219 id<NSObject> nsobj = static_cast<id<NSObject> >(obj);
223 void* CFTypeRefToNSObjectAutorelease(CFTypeRef cf_object) {
224 // When GC is on, NSMakeCollectable marks cf_object for GC and autorelease
227 // In the traditional GC-less environment, NSMakeCollectable is a no-op,
228 // and cf_object is autoreleased, balancing out the caller's ownership claim.
230 // NSMakeCollectable returns nil when used on a NULL object.
231 return [NSMakeCollectable(cf_object) autorelease];
234 static const char* base_bundle_id;
236 const char* BaseBundleID() {
237 if (base_bundle_id) {
238 return base_bundle_id;
241 #if defined(GOOGLE_CHROME_BUILD)
242 return "com.google.Chrome";
244 return "org.chromium.Chromium";
248 void SetBaseBundleID(const char* new_base_bundle_id) {
249 if (new_base_bundle_id != base_bundle_id) {
250 free((void*)base_bundle_id);
251 base_bundle_id = new_base_bundle_id ? strdup(new_base_bundle_id) : NULL;
255 // Definitions for the corresponding CF_TO_NS_CAST_DECL macros in
256 // foundation_util.h.
257 #define CF_TO_NS_CAST_DEFN(TypeCF, TypeNS) \
259 TypeNS* CFToNSCast(TypeCF##Ref cf_val) { \
260 DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val)); \
262 const_cast<TypeNS*>(reinterpret_cast<const TypeNS*>(cf_val)); \
266 TypeCF##Ref NSToCFCast(TypeNS* ns_val) { \
267 TypeCF##Ref cf_val = reinterpret_cast<TypeCF##Ref>(ns_val); \
268 DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val)); \
272 #define CF_TO_NS_MUTABLE_CAST_DEFN(name) \
273 CF_TO_NS_CAST_DEFN(CF##name, NS##name) \
275 NSMutable##name* CFToNSCast(CFMutable##name##Ref cf_val) { \
276 DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val)); \
277 NSMutable##name* ns_val = reinterpret_cast<NSMutable##name*>(cf_val); \
281 CFMutable##name##Ref NSToCFCast(NSMutable##name* ns_val) { \
282 CFMutable##name##Ref cf_val = \
283 reinterpret_cast<CFMutable##name##Ref>(ns_val); \
284 DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val)); \
288 CF_TO_NS_MUTABLE_CAST_DEFN(Array);
289 CF_TO_NS_MUTABLE_CAST_DEFN(AttributedString);
290 CF_TO_NS_CAST_DEFN(CFCalendar, NSCalendar);
291 CF_TO_NS_MUTABLE_CAST_DEFN(CharacterSet);
292 CF_TO_NS_MUTABLE_CAST_DEFN(Data);
293 CF_TO_NS_CAST_DEFN(CFDate, NSDate);
294 CF_TO_NS_MUTABLE_CAST_DEFN(Dictionary);
295 CF_TO_NS_CAST_DEFN(CFError, NSError);
296 CF_TO_NS_CAST_DEFN(CFLocale, NSLocale);
297 CF_TO_NS_CAST_DEFN(CFNumber, NSNumber);
298 CF_TO_NS_CAST_DEFN(CFRunLoopTimer, NSTimer);
299 CF_TO_NS_CAST_DEFN(CFTimeZone, NSTimeZone);
300 CF_TO_NS_MUTABLE_CAST_DEFN(Set);
301 CF_TO_NS_CAST_DEFN(CFReadStream, NSInputStream);
302 CF_TO_NS_CAST_DEFN(CFWriteStream, NSOutputStream);
303 CF_TO_NS_MUTABLE_CAST_DEFN(String);
304 CF_TO_NS_CAST_DEFN(CFURL, NSURL);
306 #undef CF_TO_NS_CAST_DEFN
307 #undef CF_TO_NS_MUTABLE_CAST_DEFN
309 #define CF_CAST_DEFN(TypeCF) \
310 template<> TypeCF##Ref \
311 CFCast<TypeCF##Ref>(const CFTypeRef& cf_val) { \
312 if (cf_val == NULL) { \
315 if (CFGetTypeID(cf_val) == TypeCF##GetTypeID()) { \
316 return (TypeCF##Ref)(cf_val); \
321 template<> TypeCF##Ref \
322 CFCastStrict<TypeCF##Ref>(const CFTypeRef& cf_val) { \
323 TypeCF##Ref rv = CFCast<TypeCF##Ref>(cf_val); \
324 DCHECK(cf_val == NULL || rv); \
328 CF_CAST_DEFN(CFArray);
330 CF_CAST_DEFN(CFBoolean);
331 CF_CAST_DEFN(CFData);
332 CF_CAST_DEFN(CFDate);
333 CF_CAST_DEFN(CFDictionary);
334 CF_CAST_DEFN(CFNull);
335 CF_CAST_DEFN(CFNumber);
337 CF_CAST_DEFN(CFString);
339 CF_CAST_DEFN(CFUUID);
341 CF_CAST_DEFN(CGColor);
343 CF_CAST_DEFN(CTFont);
347 CF_CAST_DEFN(SecACL);
348 CF_CAST_DEFN(SecTrustedApplication);
353 std::string GetValueFromDictionaryErrorMessage(
354 CFStringRef key, const std::string& expected_type, CFTypeRef value) {
355 ScopedCFTypeRef<CFStringRef> actual_type_ref(
356 CFCopyTypeIDDescription(CFGetTypeID(value)));
357 return "Expected value for key " +
358 base::SysCFStringRefToUTF8(key) +
362 base::SysCFStringRefToUTF8(actual_type_ref) +
366 NSString* FilePathToNSString(const FilePath& path) {
369 return [NSString stringWithUTF8String:path.value().c_str()];
372 FilePath NSStringToFilePath(NSString* str) {
375 return FilePath([str fileSystemRepresentation]);
381 std::ostream& operator<<(std::ostream& o, const CFStringRef string) {
382 return o << base::SysCFStringRefToUTF8(string);
385 std::ostream& operator<<(std::ostream& o, const CFErrorRef err) {
386 base::mac::ScopedCFTypeRef<CFStringRef> desc(CFErrorCopyDescription(err));
387 base::mac::ScopedCFTypeRef<CFDictionaryRef> user_info(
388 CFErrorCopyUserInfo(err));
389 CFStringRef errorDesc = NULL;
390 if (user_info.get()) {
391 errorDesc = reinterpret_cast<CFStringRef>(
392 CFDictionaryGetValue(user_info.get(), kCFErrorDescriptionKey));
394 o << "Code: " << CFErrorGetCode(err)
395 << " Domain: " << CFErrorGetDomain(err)
396 << " Desc: " << desc.get();
398 o << "(" << errorDesc << ")";