Home | History | Annotate | Download | only in mac
      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 
     12 namespace base {
     13 namespace mac {
     14 
     15 static bool g_override_am_i_bundled = false;
     16 static bool g_override_am_i_bundled_value = false;
     17 
     18 // Adapted from http://developer.apple.com/carbon/tipsandtricks.html#AmIBundled
     19 static 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 
     43 bool 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 
     55 void SetOverrideAmIBundled(bool value) {
     56   g_override_am_i_bundled = true;
     57   g_override_am_i_bundled_value = value;
     58 }
     59 
     60 bool 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.
     70 static NSBundle* g_override_app_bundle = nil;
     71 
     72 NSBundle* MainAppBundle() {
     73   if (g_override_app_bundle)
     74     return g_override_app_bundle;
     75   return [NSBundle mainBundle];
     76 }
     77 
     78 FilePath MainAppBundlePath() {
     79   NSBundle* bundle = MainAppBundle();
     80   return FilePath([[bundle bundlePath] fileSystemRepresentation]);
     81 }
     82 
     83 FilePath 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 
     92 void 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 
     99 void 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 
    107 OSType CreatorCodeForCFBundleRef(CFBundleRef bundle) {
    108   OSType creator = kUnknownType;
    109   CFBundleGetPackageInfo(bundle, NULL, &creator);
    110   return creator;
    111 }
    112 
    113 OSType CreatorCodeForApplication() {
    114   CFBundleRef bundle = CFBundleGetMainBundle();
    115   if (!bundle)
    116     return kUnknownType;
    117 
    118   return CreatorCodeForCFBundleRef(bundle);
    119 }
    120 
    121 bool 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 
    135 bool GetLocalDirectory(NSSearchPathDirectory directory, FilePath* result) {
    136   return GetSearchPathDirectory(directory, NSLocalDomainMask, result);
    137 }
    138 
    139 bool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result) {
    140   return GetSearchPathDirectory(directory, NSUserDomainMask, result);
    141 }
    142 
    143 FilePath 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
    156 FilePath 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 
    200 CFTypeRef 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 
    225 void NSObjectRetain(void* obj) {
    226   id<NSObject> nsobj = static_cast<id<NSObject> >(obj);
    227   [nsobj retain];
    228 }
    229 
    230 void NSObjectRelease(void* obj) {
    231   id<NSObject> nsobj = static_cast<id<NSObject> >(obj);
    232   [nsobj release];
    233 }
    234 
    235 }  // namespace mac
    236 }  // namespace base
    237