1 // Copyright (c) 2012 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.
5 #include "chrome/common/chrome_paths_internal.h"
7 #import <Foundation/Foundation.h>
12 #include "base/base_paths.h"
13 #include "base/logging.h"
14 #import "base/mac/foundation_util.h"
15 #import "base/mac/scoped_nsautorelease_pool.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/path_service.h"
18 #include "chrome/common/chrome_constants.h"
23 const base::FilePath* g_override_versioned_directory = NULL;
25 // Return a retained (NOT autoreleased) NSBundle* as the internal
26 // implementation of chrome::OuterAppBundle(), which should be the only
28 NSBundle* OuterAppBundleInternal() {
29 base::mac::ScopedNSAutoreleasePool pool;
31 if (!base::mac::AmIBundled()) {
32 // If unbundled (as in a test), there's no app bundle.
36 if (!base::mac::IsBackgroundOnlyProcess()) {
37 // Shortcut: in the browser process, just return the main app bundle.
38 return [[NSBundle mainBundle] retain];
41 // From C.app/Contents/Versions/1.2.3.4, go up three steps to get to C.app.
42 base::FilePath versioned_dir = chrome::GetVersionedDirectory();
43 base::FilePath outer_app_dir = versioned_dir.DirName().DirName().DirName();
44 const char* outer_app_dir_c = outer_app_dir.value().c_str();
45 NSString* outer_app_dir_ns = [NSString stringWithUTF8String:outer_app_dir_c];
47 return [[NSBundle bundleWithPath:outer_app_dir_ns] retain];
49 #endif // !defined(OS_IOS)
51 char* ProductDirNameForBundle(NSBundle* chrome_bundle) {
52 const char* product_dir_name = NULL;
54 base::mac::ScopedNSAutoreleasePool pool;
56 NSString* product_dir_name_ns =
57 [chrome_bundle objectForInfoDictionaryKey:@"CrProductDirName"];
58 product_dir_name = [product_dir_name_ns fileSystemRepresentation];
60 DCHECK(!chrome_bundle);
63 if (!product_dir_name) {
64 #if defined(GOOGLE_CHROME_BUILD)
65 product_dir_name = "Google/Chrome";
67 product_dir_name = "Chromium";
71 // Leaked, but the only caller initializes a static with this result, so it
72 // only happens once, and that's OK.
73 return strdup(product_dir_name);
76 // ProductDirName returns the name of the directory inside
77 // ~/Library/Application Support that should hold the product application
78 // data. This can be overridden by setting the CrProductDirName key in the
79 // outer browser .app's Info.plist. The default is "Google/Chrome" for
80 // officially-branded builds, and "Chromium" for unbranded builds. For the
81 // official canary channel, the Info.plist will have CrProductDirName set
82 // to "Google/Chrome Canary".
83 std::string ProductDirName() {
85 static const char* product_dir_name = ProductDirNameForBundle(nil);
87 // Use OuterAppBundle() to get the main app's bundle. This key needs to live
88 // in the main app's bundle because it will be set differently on the canary
89 // channel, and the autoupdate system dictates that there can be no
90 // differences between channels within the versioned directory. This would
91 // normally use base::mac::FrameworkBundle(), but that references the
92 // framework bundle within the versioned directory. Ordinarily, the profile
93 // should not be accessed from non-browser processes, but those processes do
94 // attempt to get the profile directory, so direct them to look in the outer
95 // browser .app's Info.plist for the CrProductDirName key.
96 static const char* product_dir_name =
97 ProductDirNameForBundle(chrome::OuterAppBundle());
99 return std::string(product_dir_name);
102 bool GetDefaultUserDataDirectoryForProduct(const std::string& product_dir,
103 base::FilePath* result) {
104 bool success = false;
105 if (result && PathService::Get(base::DIR_APP_DATA, result)) {
106 *result = result->Append(product_dir);
116 bool GetDefaultUserDataDirectory(base::FilePath* result) {
117 return GetDefaultUserDataDirectoryForProduct(ProductDirName(), result);
120 bool GetUserDocumentsDirectory(base::FilePath* result) {
121 return base::mac::GetUserDirectory(NSDocumentDirectory, result);
124 void GetUserCacheDirectory(const base::FilePath& profile_dir,
125 base::FilePath* result) {
126 // If the profile directory is under ~/Library/Application Support,
127 // use a suitable cache directory under ~/Library/Caches. For
128 // example, a profile directory of ~/Library/Application
129 // Support/Google/Chrome/MyProfileName would use the cache directory
130 // ~/Library/Caches/Google/Chrome/MyProfileName.
132 // Default value in cases where any of the following fails.
133 *result = profile_dir;
135 base::FilePath app_data_dir;
136 if (!PathService::Get(base::DIR_APP_DATA, &app_data_dir))
138 base::FilePath cache_dir;
139 if (!PathService::Get(base::DIR_CACHE, &cache_dir))
141 if (!app_data_dir.AppendRelativePath(profile_dir, &cache_dir))
147 bool GetUserDownloadsDirectory(base::FilePath* result) {
148 return base::mac::GetUserDirectory(NSDownloadsDirectory, result);
151 bool GetUserMusicDirectory(base::FilePath* result) {
152 return base::mac::GetUserDirectory(NSMusicDirectory, result);
155 bool GetUserPicturesDirectory(base::FilePath* result) {
156 return base::mac::GetUserDirectory(NSPicturesDirectory, result);
159 bool GetUserVideosDirectory(base::FilePath* result) {
160 return base::mac::GetUserDirectory(NSMoviesDirectory, result);
165 base::FilePath GetVersionedDirectory() {
166 if (g_override_versioned_directory)
167 return *g_override_versioned_directory;
169 // Start out with the path to the running executable.
171 PathService::Get(base::FILE_EXE, &path);
173 // One step up to MacOS, another to Contents.
174 path = path.DirName().DirName();
175 DCHECK_EQ(path.BaseName().value(), "Contents");
177 if (base::mac::IsBackgroundOnlyProcess()) {
178 // path identifies the helper .app's Contents directory in the browser
179 // .app's versioned directory. Go up two steps to get to the browser
180 // .app's versioned directory.
181 path = path.DirName().DirName();
182 DCHECK_EQ(path.BaseName().value(), kChromeVersion);
184 // Go into the versioned directory.
185 path = path.Append("Versions").Append(kChromeVersion);
191 void SetOverrideVersionedDirectory(const base::FilePath* path) {
192 if (path != g_override_versioned_directory) {
193 delete g_override_versioned_directory;
194 g_override_versioned_directory = path;
198 base::FilePath GetFrameworkBundlePath() {
199 // It's tempting to use +[NSBundle bundleWithIdentifier:], but it's really
200 // slow (about 30ms on 10.5 and 10.6), despite Apple's documentation stating
201 // that it may be more efficient than +bundleForClass:. +bundleForClass:
202 // itself takes 1-2ms. Getting an NSBundle from a path, on the other hand,
203 // essentially takes no time at all, at least when the bundle has already
204 // been loaded as it will have been in this case. The FilePath operations
205 // needed to compute the framework's path are also effectively free, so that
206 // is the approach that is used here. NSBundle is also documented as being
207 // not thread-safe, and thread safety may be a concern here.
209 // The framework bundle is at a known path and name from the browser .app's
210 // versioned directory.
211 return GetVersionedDirectory().Append(kFrameworkName);
214 bool GetLocalLibraryDirectory(base::FilePath* result) {
215 return base::mac::GetLocalDirectory(NSLibraryDirectory, result);
218 bool GetUserLibraryDirectory(base::FilePath* result) {
219 return base::mac::GetUserDirectory(NSLibraryDirectory, result);
222 bool GetUserApplicationsDirectory(base::FilePath* result) {
223 return base::mac::GetUserDirectory(NSApplicationDirectory, result);
226 bool GetGlobalApplicationSupportDirectory(base::FilePath* result) {
227 return base::mac::GetLocalDirectory(NSApplicationSupportDirectory, result);
230 NSBundle* OuterAppBundle() {
231 // Cache this. Foundation leaks it anyway, and this should be the only call
232 // to OuterAppBundleInternal().
233 static NSBundle* bundle = OuterAppBundleInternal();
237 bool GetUserDataDirectoryForBrowserBundle(NSBundle* bundle,
238 base::FilePath* result) {
239 scoped_ptr<char, base::FreeDeleter>
240 product_dir_name(ProductDirNameForBundle(bundle));
241 return GetDefaultUserDataDirectoryForProduct(product_dir_name.get(), result);
244 #endif // !defined(OS_IOS)
246 bool ProcessNeedsProfileDir(const std::string& process_type) {
247 // For now we have no reason to forbid this on other MacOS as we don't
248 // have the roaming profile troubles there.
252 } // namespace chrome