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 20namespace { 21 22const FilePath* g_override_versioned_directory = NULL; 23 24NSBundle* 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 44const 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". 81std::string ProductDirName() { 82 static const char* product_dir_name = ProductDirNameInternal(); 83 return std::string(product_dir_name); 84} 85 86} // namespace 87 88namespace chrome { 89 90bool 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 99bool GetUserDocumentsDirectory(FilePath* result) { 100 return base::mac::GetUserDirectory(NSDocumentDirectory, result); 101} 102 103void 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 125bool GetUserDownloadsDirectory(FilePath* result) { 126 return base::mac::GetUserDirectory(NSDownloadsDirectory, result); 127} 128 129bool GetUserDesktop(FilePath* result) { 130 return base::mac::GetUserDirectory(NSDesktopDirectory, result); 131} 132 133FilePath 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 159void 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 166FilePath 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 182bool GetLocalLibraryDirectory(FilePath* result) { 183 return base::mac::GetLocalDirectory(NSLibraryDirectory, result); 184} 185 186} // namespace chrome 187