// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/mac/foundation_util.h" #include "base/file_path.h" #include "base/logging.h" #include "base/mac/scoped_cftyperef.h" #include "base/sys_string_conversions.h" namespace base { namespace mac { static bool g_override_am_i_bundled = false; static bool g_override_am_i_bundled_value = false; // Adapted from http://developer.apple.com/carbon/tipsandtricks.html#AmIBundled static bool UncachedAmIBundled() { if (g_override_am_i_bundled) return g_override_am_i_bundled_value; ProcessSerialNumber psn = {0, kCurrentProcess}; FSRef fsref; OSStatus pbErr; if ((pbErr = GetProcessBundleLocation(&psn, &fsref)) != noErr) { LOG(ERROR) << "GetProcessBundleLocation failed: error " << pbErr; return false; } FSCatalogInfo info; OSErr fsErr; if ((fsErr = FSGetCatalogInfo(&fsref, kFSCatInfoNodeFlags, &info, NULL, NULL, NULL)) != noErr) { LOG(ERROR) << "FSGetCatalogInfo failed: error " << fsErr; return false; } return info.nodeFlags & kFSNodeIsDirectoryMask; } bool AmIBundled() { // If the return value is not cached, this function will return different // values depending on when it's called. This confuses some client code, see // http://crbug.com/63183 . static bool result = UncachedAmIBundled(); DCHECK_EQ(result, UncachedAmIBundled()) << "The return value of AmIBundled() changed. This will confuse tests. " << "Call SetAmIBundled() override manually if your test binary " << "delay-loads the framework."; return result; } void SetOverrideAmIBundled(bool value) { g_override_am_i_bundled = true; g_override_am_i_bundled_value = value; } bool IsBackgroundOnlyProcess() { // This function really does want to examine NSBundle's idea of the main // bundle dictionary, and not the overriden MainAppBundle. It needs to look // at the actual running .app's Info.plist to access its LSUIElement // property. NSDictionary* info_dictionary = [[NSBundle mainBundle] infoDictionary]; return [[info_dictionary objectForKey:@"LSUIElement"] boolValue] != NO; } // No threading worries since NSBundle isn't thread safe. static NSBundle* g_override_app_bundle = nil; NSBundle* MainAppBundle() { if (g_override_app_bundle) return g_override_app_bundle; return [NSBundle mainBundle]; } FilePath MainAppBundlePath() { NSBundle* bundle = MainAppBundle(); return FilePath([[bundle bundlePath] fileSystemRepresentation]); } FilePath PathForMainAppBundleResource(CFStringRef resourceName) { NSBundle* bundle = MainAppBundle(); NSString* resourcePath = [bundle pathForResource:(NSString*)resourceName ofType:nil]; if (!resourcePath) return FilePath(); return FilePath([resourcePath fileSystemRepresentation]); } void SetOverrideAppBundle(NSBundle* bundle) { if (bundle != g_override_app_bundle) { [g_override_app_bundle release]; g_override_app_bundle = [bundle retain]; } } void SetOverrideAppBundlePath(const FilePath& file_path) { NSString* path = base::SysUTF8ToNSString(file_path.value()); NSBundle* bundle = [NSBundle bundleWithPath:path]; CHECK(bundle) << "Failed to load the bundle at " << file_path.value(); SetOverrideAppBundle(bundle); } OSType CreatorCodeForCFBundleRef(CFBundleRef bundle) { OSType creator = kUnknownType; CFBundleGetPackageInfo(bundle, NULL, &creator); return creator; } OSType CreatorCodeForApplication() { CFBundleRef bundle = CFBundleGetMainBundle(); if (!bundle) return kUnknownType; return CreatorCodeForCFBundleRef(bundle); } bool GetSearchPathDirectory(NSSearchPathDirectory directory, NSSearchPathDomainMask domain_mask, FilePath* result) { DCHECK(result); NSArray* dirs = NSSearchPathForDirectoriesInDomains(directory, domain_mask, YES); if ([dirs count] < 1) { return false; } NSString* path = [dirs objectAtIndex:0]; *result = FilePath([path fileSystemRepresentation]); return true; } bool GetLocalDirectory(NSSearchPathDirectory directory, FilePath* result) { return GetSearchPathDirectory(directory, NSLocalDomainMask, result); } bool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result) { return GetSearchPathDirectory(directory, NSUserDomainMask, result); } FilePath GetUserLibraryPath() { FilePath user_library_path; if (!GetUserDirectory(NSLibraryDirectory, &user_library_path)) { LOG(WARNING) << "Could not get user library path"; } return user_library_path; } // Takes a path to an (executable) binary and tries to provide the path to an // application bundle containing it. It takes the outermost bundle that it can // find (so for "/Foo/Bar.app/.../Baz.app/..." it produces "/Foo/Bar.app"). // |exec_name| - path to the binary // returns - path to the application bundle, or empty on error FilePath GetAppBundlePath(const FilePath& exec_name) { const char kExt[] = ".app"; const size_t kExtLength = arraysize(kExt) - 1; // Split the path into components. std::vector<std::string> components; exec_name.GetComponents(&components); // It's an error if we don't get any components. if (!components.size()) return FilePath(); // Don't prepend '/' to the first component. std::vector<std::string>::const_iterator it = components.begin(); std::string bundle_name = *it; DCHECK_GT(it->length(), 0U); // If the first component ends in ".app", we're already done. if (it->length() > kExtLength && !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength)) return FilePath(bundle_name); // The first component may be "/" or "//", etc. Only append '/' if it doesn't // already end in '/'. if (bundle_name[bundle_name.length() - 1] != '/') bundle_name += '/'; // Go through the remaining components. for (++it; it != components.end(); ++it) { DCHECK_GT(it->length(), 0U); bundle_name += *it; // If the current component ends in ".app", we're done. if (it->length() > kExtLength && !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength)) return FilePath(bundle_name); // Separate this component from the next one. bundle_name += '/'; } return FilePath(); } CFTypeRef GetValueFromDictionary(CFDictionaryRef dict, CFStringRef key, CFTypeID expected_type) { CFTypeRef value = CFDictionaryGetValue(dict, key); if (!value) return value; if (CFGetTypeID(value) != expected_type) { ScopedCFTypeRef<CFStringRef> expected_type_ref( CFCopyTypeIDDescription(expected_type)); ScopedCFTypeRef<CFStringRef> actual_type_ref( CFCopyTypeIDDescription(CFGetTypeID(value))); LOG(WARNING) << "Expected value for key " << base::SysCFStringRefToUTF8(key) << " to be " << base::SysCFStringRefToUTF8(expected_type_ref) << " but it was " << base::SysCFStringRefToUTF8(actual_type_ref) << " instead"; return NULL; } return value; } void NSObjectRetain(void* obj) { id<NSObject> nsobj = static_cast<id<NSObject> >(obj); [nsobj retain]; } void NSObjectRelease(void* obj) { id<NSObject> nsobj = static_cast<id<NSObject> >(obj); [nsobj release]; } } // namespace mac } // namespace base