Home | History | Annotate | Download | only in common
      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 "chrome/common/chrome_paths_internal.h"
      6 
      7 #import <Foundation/Foundation.h>
      8 #include <string.h>
      9 
     10 #include <string>
     11 
     12 #include "base/base_paths.h"
     13 #include "base/logging.h"
     14 #import "base/mac/foundation_util.h"
     15 #import "base/mac/mac_util.h"
     16 #import "base/mac/scoped_nsautorelease_pool.h"
     17 #include "base/path_service.h"
     18 #include "chrome/common/chrome_constants.h"
     19 
     20 namespace {
     21 
     22 const FilePath* g_override_versioned_directory = NULL;
     23 
     24 NSBundle* OuterAppBundle() {
     25   if (!base::mac::AmIBundled()) {
     26     // If unbundled (as in a test), there's no app bundle.
     27     return nil;
     28   }
     29 
     30   if (!base::mac::IsBackgroundOnlyProcess()) {
     31     // Shortcut: in the browser process, just return the main app bundle.
     32     return [NSBundle mainBundle];
     33   }
     34 
     35   // From C.app/Contents/Versions/1.2.3.4, go up three steps to get to C.app.
     36   FilePath versioned_dir = chrome::GetVersionedDirectory();
     37   FilePath outer_app_dir = versioned_dir.DirName().DirName().DirName();
     38   const char* outer_app_dir_c = outer_app_dir.value().c_str();
     39   NSString* outer_app_dir_ns = [NSString stringWithUTF8String:outer_app_dir_c];
     40 
     41   return [NSBundle bundleWithPath:outer_app_dir_ns];
     42 }
     43 
     44 const char* ProductDirNameInternal() {
     45   base::mac::ScopedNSAutoreleasePool pool;
     46 
     47   // Use OuterAppBundle() to get the main app's bundle. This key needs to live
     48   // in the main app's bundle because it will be set differently on the canary
     49   // channel, and the autoupdate system dictates that there can be no
     50   // differences between channels within the versioned directory. This would
     51   // normally use base::mac::MainAppBundle(), but that references the
     52   // framework bundle within the versioned directory. Ordinarily, the profile
     53   // should not be accessed from non-browser processes, but those processes do
     54   // attempt to get the profile directory, so direct them to look in the outer
     55   // browser .app's Info.plist for the CrProductDirName key.
     56   NSBundle* bundle = OuterAppBundle();
     57   NSString* product_dir_name_ns =
     58       [bundle objectForInfoDictionaryKey:@"CrProductDirName"];
     59   const char* product_dir_name = [product_dir_name_ns fileSystemRepresentation];
     60 
     61   if (!product_dir_name) {
     62 #if defined(GOOGLE_CHROME_BUILD)
     63     product_dir_name = "Google/Chrome";
     64 #else
     65     product_dir_name = "Chromium";
     66 #endif
     67   }
     68 
     69   // Leaked, but the only caller initializes a static with this result, so it
     70   // only happens once, and that's OK.
     71   return strdup(product_dir_name);
     72 }
     73 
     74 // ProductDirName returns the name of the directory inside
     75 // ~/Library/Application Support that should hold the product application
     76 // data. This can be overridden by setting the CrProductDirName key in the
     77 // outer browser .app's Info.plist. The default is "Google/Chrome" for
     78 // officially-branded builds, and "Chromium" for unbranded builds. For the
     79 // official canary channel, the Info.plist will have CrProductDirName set
     80 // to "Google/Chrome Canary".
     81 std::string ProductDirName() {
     82   static const char* product_dir_name = ProductDirNameInternal();
     83   return std::string(product_dir_name);
     84 }
     85 
     86 }  // namespace
     87 
     88 namespace chrome {
     89 
     90 bool GetDefaultUserDataDirectory(FilePath* result) {
     91   bool success = false;
     92   if (result && PathService::Get(base::DIR_APP_DATA, result)) {
     93     *result = result->Append(ProductDirName());
     94     success = true;
     95   }
     96   return success;
     97 }
     98 
     99 bool GetUserDocumentsDirectory(FilePath* result) {
    100   return base::mac::GetUserDirectory(NSDocumentDirectory, result);
    101 }
    102 
    103 void GetUserCacheDirectory(const FilePath& profile_dir, FilePath* result) {
    104   // If the profile directory is under ~/Library/Application Support,
    105   // use a suitable cache directory under ~/Library/Caches.  For
    106   // example, a profile directory of ~/Library/Application
    107   // Support/Google/Chrome/MyProfileName would use the cache directory
    108   // ~/Library/Caches/Google/Chrome/MyProfileName.
    109 
    110   // Default value in cases where any of the following fails.
    111   *result = profile_dir;
    112 
    113   FilePath app_data_dir;
    114   if (!PathService::Get(base::DIR_APP_DATA, &app_data_dir))
    115     return;
    116   FilePath cache_dir;
    117   if (!PathService::Get(base::DIR_CACHE, &cache_dir))
    118     return;
    119   if (!app_data_dir.AppendRelativePath(profile_dir, &cache_dir))
    120     return;
    121 
    122   *result = cache_dir;
    123 }
    124 
    125 bool GetUserDownloadsDirectory(FilePath* result) {
    126   return base::mac::GetUserDirectory(NSDownloadsDirectory, result);
    127 }
    128 
    129 bool GetUserDesktop(FilePath* result) {
    130   return base::mac::GetUserDirectory(NSDesktopDirectory, result);
    131 }
    132 
    133 FilePath GetVersionedDirectory() {
    134   if (g_override_versioned_directory)
    135     return *g_override_versioned_directory;
    136 
    137   // Start out with the path to the running executable.
    138   FilePath path;
    139   PathService::Get(base::FILE_EXE, &path);
    140 
    141   // One step up to MacOS, another to Contents.
    142   path = path.DirName().DirName();
    143   DCHECK_EQ(path.BaseName().value(), "Contents");
    144 
    145   if (base::mac::IsBackgroundOnlyProcess()) {
    146     // path identifies the helper .app's Contents directory in the browser
    147     // .app's versioned directory.  Go up two steps to get to the browser
    148     // .app's versioned directory.
    149     path = path.DirName().DirName();
    150     DCHECK_EQ(path.BaseName().value(), kChromeVersion);
    151   } else {
    152     // Go into the versioned directory.
    153     path = path.Append("Versions").Append(kChromeVersion);
    154   }
    155 
    156   return path;
    157 }
    158 
    159 void SetOverrideVersionedDirectory(const FilePath* path) {
    160   if (path != g_override_versioned_directory) {
    161     delete g_override_versioned_directory;
    162     g_override_versioned_directory = path;
    163   }
    164 }
    165 
    166 FilePath GetFrameworkBundlePath() {
    167   // It's tempting to use +[NSBundle bundleWithIdentifier:], but it's really
    168   // slow (about 30ms on 10.5 and 10.6), despite Apple's documentation stating
    169   // that it may be more efficient than +bundleForClass:.  +bundleForClass:
    170   // itself takes 1-2ms.  Getting an NSBundle from a path, on the other hand,
    171   // essentially takes no time at all, at least when the bundle has already
    172   // been loaded as it will have been in this case.  The FilePath operations
    173   // needed to compute the framework's path are also effectively free, so that
    174   // is the approach that is used here.  NSBundle is also documented as being
    175   // not thread-safe, and thread safety may be a concern here.
    176 
    177   // The framework bundle is at a known path and name from the browser .app's
    178   // versioned directory.
    179   return GetVersionedDirectory().Append(kFrameworkName);
    180 }
    181 
    182 bool GetLocalLibraryDirectory(FilePath* result) {
    183   return base::mac::GetLocalDirectory(NSLibraryDirectory, result);
    184 }
    185 
    186 }  // namespace chrome
    187