blob: 3f41466e9c90e4ca84b1889a0d6b58ecf21577af [file] [log] [blame]
// 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