foundation_util.mm revision ddb351dbec246cf1fab5ec20d2d5520909041de1
1// Copyright (c) 2011 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.
4
5#include "base/mac/foundation_util.h"
6
7#include "base/file_path.h"
8#include "base/logging.h"
9#include "base/mac/scoped_cftyperef.h"
10#include "base/sys_string_conversions.h"
11
12namespace base {
13namespace mac {
14
15static bool g_override_am_i_bundled = false;
16static bool g_override_am_i_bundled_value = false;
17
18// Adapted from http://developer.apple.com/carbon/tipsandtricks.html#AmIBundled
19static bool UncachedAmIBundled() {
20  if (g_override_am_i_bundled)
21    return g_override_am_i_bundled_value;
22
23  ProcessSerialNumber psn = {0, kCurrentProcess};
24
25  FSRef fsref;
26  OSStatus pbErr;
27  if ((pbErr = GetProcessBundleLocation(&psn, &fsref)) != noErr) {
28    LOG(ERROR) << "GetProcessBundleLocation failed: error " << pbErr;
29    return false;
30  }
31
32  FSCatalogInfo info;
33  OSErr fsErr;
34  if ((fsErr = FSGetCatalogInfo(&fsref, kFSCatInfoNodeFlags, &info,
35                                NULL, NULL, NULL)) != noErr) {
36    LOG(ERROR) << "FSGetCatalogInfo failed: error " << fsErr;
37    return false;
38  }
39
40  return info.nodeFlags & kFSNodeIsDirectoryMask;
41}
42
43bool AmIBundled() {
44  // If the return value is not cached, this function will return different
45  // values depending on when it's called. This confuses some client code, see
46  // http://crbug.com/63183 .
47  static bool result = UncachedAmIBundled();
48  DCHECK_EQ(result, UncachedAmIBundled())
49      << "The return value of AmIBundled() changed. This will confuse tests. "
50      << "Call SetAmIBundled() override manually if your test binary "
51      << "delay-loads the framework.";
52  return result;
53}
54
55void SetOverrideAmIBundled(bool value) {
56  g_override_am_i_bundled = true;
57  g_override_am_i_bundled_value = value;
58}
59
60bool IsBackgroundOnlyProcess() {
61  // This function really does want to examine NSBundle's idea of the main
62  // bundle dictionary, and not the overriden MainAppBundle.  It needs to look
63  // at the actual running .app's Info.plist to access its LSUIElement
64  // property.
65  NSDictionary* info_dictionary = [[NSBundle mainBundle] infoDictionary];
66  return [[info_dictionary objectForKey:@"LSUIElement"] boolValue] != NO;
67}
68
69// No threading worries since NSBundle isn't thread safe.
70static NSBundle* g_override_app_bundle = nil;
71
72NSBundle* MainAppBundle() {
73  if (g_override_app_bundle)
74    return g_override_app_bundle;
75  return [NSBundle mainBundle];
76}
77
78FilePath MainAppBundlePath() {
79  NSBundle* bundle = MainAppBundle();
80  return FilePath([[bundle bundlePath] fileSystemRepresentation]);
81}
82
83FilePath PathForMainAppBundleResource(CFStringRef resourceName) {
84  NSBundle* bundle = MainAppBundle();
85  NSString* resourcePath = [bundle pathForResource:(NSString*)resourceName
86                                            ofType:nil];
87  if (!resourcePath)
88    return FilePath();
89  return FilePath([resourcePath fileSystemRepresentation]);
90}
91
92void SetOverrideAppBundle(NSBundle* bundle) {
93  if (bundle != g_override_app_bundle) {
94    [g_override_app_bundle release];
95    g_override_app_bundle = [bundle retain];
96  }
97}
98
99void SetOverrideAppBundlePath(const FilePath& file_path) {
100  NSString* path = base::SysUTF8ToNSString(file_path.value());
101  NSBundle* bundle = [NSBundle bundleWithPath:path];
102  CHECK(bundle) << "Failed to load the bundle at " << file_path.value();
103
104  SetOverrideAppBundle(bundle);
105}
106
107OSType CreatorCodeForCFBundleRef(CFBundleRef bundle) {
108  OSType creator = kUnknownType;
109  CFBundleGetPackageInfo(bundle, NULL, &creator);
110  return creator;
111}
112
113OSType CreatorCodeForApplication() {
114  CFBundleRef bundle = CFBundleGetMainBundle();
115  if (!bundle)
116    return kUnknownType;
117
118  return CreatorCodeForCFBundleRef(bundle);
119}
120
121bool GetSearchPathDirectory(NSSearchPathDirectory directory,
122                            NSSearchPathDomainMask domain_mask,
123                            FilePath* result) {
124  DCHECK(result);
125  NSArray* dirs =
126      NSSearchPathForDirectoriesInDomains(directory, domain_mask, YES);
127  if ([dirs count] < 1) {
128    return false;
129  }
130  NSString* path = [dirs objectAtIndex:0];
131  *result = FilePath([path fileSystemRepresentation]);
132  return true;
133}
134
135bool GetLocalDirectory(NSSearchPathDirectory directory, FilePath* result) {
136  return GetSearchPathDirectory(directory, NSLocalDomainMask, result);
137}
138
139bool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result) {
140  return GetSearchPathDirectory(directory, NSUserDomainMask, result);
141}
142
143FilePath GetUserLibraryPath() {
144  FilePath user_library_path;
145  if (!GetUserDirectory(NSLibraryDirectory, &user_library_path)) {
146    LOG(WARNING) << "Could not get user library path";
147  }
148  return user_library_path;
149}
150
151// Takes a path to an (executable) binary and tries to provide the path to an
152// application bundle containing it. It takes the outermost bundle that it can
153// find (so for "/Foo/Bar.app/.../Baz.app/..." it produces "/Foo/Bar.app").
154//   |exec_name| - path to the binary
155//   returns - path to the application bundle, or empty on error
156FilePath GetAppBundlePath(const FilePath& exec_name) {
157  const char kExt[] = ".app";
158  const size_t kExtLength = arraysize(kExt) - 1;
159
160  // Split the path into components.
161  std::vector<std::string> components;
162  exec_name.GetComponents(&components);
163
164  // It's an error if we don't get any components.
165  if (!components.size())
166    return FilePath();
167
168  // Don't prepend '/' to the first component.
169  std::vector<std::string>::const_iterator it = components.begin();
170  std::string bundle_name = *it;
171  DCHECK_GT(it->length(), 0U);
172  // If the first component ends in ".app", we're already done.
173  if (it->length() > kExtLength &&
174      !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength))
175    return FilePath(bundle_name);
176
177  // The first component may be "/" or "//", etc. Only append '/' if it doesn't
178  // already end in '/'.
179  if (bundle_name[bundle_name.length() - 1] != '/')
180    bundle_name += '/';
181
182  // Go through the remaining components.
183  for (++it; it != components.end(); ++it) {
184    DCHECK_GT(it->length(), 0U);
185
186    bundle_name += *it;
187
188    // If the current component ends in ".app", we're done.
189    if (it->length() > kExtLength &&
190        !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength))
191      return FilePath(bundle_name);
192
193    // Separate this component from the next one.
194    bundle_name += '/';
195  }
196
197  return FilePath();
198}
199
200CFTypeRef GetValueFromDictionary(CFDictionaryRef dict,
201                                 CFStringRef key,
202                                 CFTypeID expected_type) {
203  CFTypeRef value = CFDictionaryGetValue(dict, key);
204  if (!value)
205    return value;
206
207  if (CFGetTypeID(value) != expected_type) {
208    ScopedCFTypeRef<CFStringRef> expected_type_ref(
209        CFCopyTypeIDDescription(expected_type));
210    ScopedCFTypeRef<CFStringRef> actual_type_ref(
211        CFCopyTypeIDDescription(CFGetTypeID(value)));
212    LOG(WARNING) << "Expected value for key "
213                 << base::SysCFStringRefToUTF8(key)
214                 << " to be "
215                 << base::SysCFStringRefToUTF8(expected_type_ref)
216                 << " but it was "
217                 << base::SysCFStringRefToUTF8(actual_type_ref)
218                 << " instead";
219    return NULL;
220  }
221
222  return value;
223}
224
225void NSObjectRetain(void* obj) {
226  id<NSObject> nsobj = static_cast<id<NSObject> >(obj);
227  [nsobj retain];
228}
229
230void NSObjectRelease(void* obj) {
231  id<NSObject> nsobj = static_cast<id<NSObject> >(obj);
232  [nsobj release];
233}
234
235}  // namespace mac
236}  // namespace base
237