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/path_service.h"
17 #include "chrome/common/chrome_constants.h"
20 #import "base/mac/mac_util.h"
26 const FilePath* g_override_versioned_directory = NULL;
28 // Return a retained (NOT autoreleased) NSBundle* as the internal
29 // implementation of chrome::OuterAppBundle(), which should be the only
31 NSBundle* OuterAppBundleInternal() {
32 base::mac::ScopedNSAutoreleasePool pool;
34 if (!base::mac::AmIBundled()) {
35 // If unbundled (as in a test), there's no app bundle.
39 if (!base::mac::IsBackgroundOnlyProcess()) {
40 // Shortcut: in the browser process, just return the main app bundle.
41 return [[NSBundle mainBundle] retain];
44 // From C.app/Contents/Versions/1.2.3.4, go up three steps to get to C.app.
45 FilePath versioned_dir = chrome::GetVersionedDirectory();
46 FilePath outer_app_dir = versioned_dir.DirName().DirName().DirName();
47 const char* outer_app_dir_c = outer_app_dir.value().c_str();
48 NSString* outer_app_dir_ns = [NSString stringWithUTF8String:outer_app_dir_c];
50 return [[NSBundle bundleWithPath:outer_app_dir_ns] retain];
52 #endif // !defined(OS_IOS)
54 const char* ProductDirNameInternal() {
55 const char* product_dir_name = NULL;
57 base::mac::ScopedNSAutoreleasePool pool;
59 // Use OuterAppBundle() to get the main app's bundle. This key needs to live
60 // in the main app's bundle because it will be set differently on the canary
61 // channel, and the autoupdate system dictates that there can be no
62 // differences between channels within the versioned directory. This would
63 // normally use base::mac::FrameworkBundle(), but that references the
64 // framework bundle within the versioned directory. Ordinarily, the profile
65 // should not be accessed from non-browser processes, but those processes do
66 // attempt to get the profile directory, so direct them to look in the outer
67 // browser .app's Info.plist for the CrProductDirName key.
68 NSBundle* bundle = chrome::OuterAppBundle();
69 NSString* product_dir_name_ns =
70 [bundle objectForInfoDictionaryKey:@"CrProductDirName"];
71 product_dir_name = [product_dir_name_ns fileSystemRepresentation];
74 if (!product_dir_name) {
75 #if defined(GOOGLE_CHROME_BUILD)
76 product_dir_name = "Google/Chrome";
78 product_dir_name = "Chromium";
82 // Leaked, but the only caller initializes a static with this result, so it
83 // only happens once, and that's OK.
84 return strdup(product_dir_name);
87 // ProductDirName returns the name of the directory inside
88 // ~/Library/Application Support that should hold the product application
89 // data. This can be overridden by setting the CrProductDirName key in the
90 // outer browser .app's Info.plist. The default is "Google/Chrome" for
91 // officially-branded builds, and "Chromium" for unbranded builds. For the
92 // official canary channel, the Info.plist will have CrProductDirName set
93 // to "Google/Chrome Canary".
94 std::string ProductDirName() {
95 static const char* product_dir_name = ProductDirNameInternal();
96 return std::string(product_dir_name);
103 bool GetDefaultUserDataDirectory(FilePath* result) {
104 bool success = false;
105 if (result && PathService::Get(base::DIR_APP_DATA, result)) {
106 *result = result->Append(ProductDirName());
112 bool GetUserDocumentsDirectory(FilePath* result) {
113 return base::mac::GetUserDirectory(NSDocumentDirectory, result);
116 void GetUserCacheDirectory(const FilePath& profile_dir, FilePath* result) {
117 // If the profile directory is under ~/Library/Application Support,
118 // use a suitable cache directory under ~/Library/Caches. For
119 // example, a profile directory of ~/Library/Application
120 // Support/Google/Chrome/MyProfileName would use the cache directory
121 // ~/Library/Caches/Google/Chrome/MyProfileName.
123 // Default value in cases where any of the following fails.
124 *result = profile_dir;
126 FilePath app_data_dir;
127 if (!PathService::Get(base::DIR_APP_DATA, &app_data_dir))
130 if (!PathService::Get(base::DIR_CACHE, &cache_dir))
132 if (!app_data_dir.AppendRelativePath(profile_dir, &cache_dir))
138 bool GetUserDownloadsDirectory(FilePath* result) {
139 return base::mac::GetUserDirectory(NSDownloadsDirectory, result);
142 bool GetUserMusicDirectory(FilePath* result) {
143 return base::mac::GetUserDirectory(NSMusicDirectory, result);
146 bool GetUserPicturesDirectory(FilePath* result) {
147 return base::mac::GetUserDirectory(NSPicturesDirectory, result);
150 bool GetUserVideosDirectory(FilePath* result) {
151 return base::mac::GetUserDirectory(NSMoviesDirectory, result);
156 FilePath GetVersionedDirectory() {
157 if (g_override_versioned_directory)
158 return *g_override_versioned_directory;
160 // Start out with the path to the running executable.
162 PathService::Get(base::FILE_EXE, &path);
164 // One step up to MacOS, another to Contents.
165 path = path.DirName().DirName();
166 DCHECK_EQ(path.BaseName().value(), "Contents");
168 if (base::mac::IsBackgroundOnlyProcess()) {
169 // path identifies the helper .app's Contents directory in the browser
170 // .app's versioned directory. Go up two steps to get to the browser
171 // .app's versioned directory.
172 path = path.DirName().DirName();
173 DCHECK_EQ(path.BaseName().value(), kChromeVersion);
175 // Go into the versioned directory.
176 path = path.Append("Versions").Append(kChromeVersion);
182 void SetOverrideVersionedDirectory(const FilePath* path) {
183 if (path != g_override_versioned_directory) {
184 delete g_override_versioned_directory;
185 g_override_versioned_directory = path;
189 FilePath GetFrameworkBundlePath() {
190 // It's tempting to use +[NSBundle bundleWithIdentifier:], but it's really
191 // slow (about 30ms on 10.5 and 10.6), despite Apple's documentation stating
192 // that it may be more efficient than +bundleForClass:. +bundleForClass:
193 // itself takes 1-2ms. Getting an NSBundle from a path, on the other hand,
194 // essentially takes no time at all, at least when the bundle has already
195 // been loaded as it will have been in this case. The FilePath operations
196 // needed to compute the framework's path are also effectively free, so that
197 // is the approach that is used here. NSBundle is also documented as being
198 // not thread-safe, and thread safety may be a concern here.
200 // The framework bundle is at a known path and name from the browser .app's
201 // versioned directory.
202 return GetVersionedDirectory().Append(kFrameworkName);
205 bool GetLocalLibraryDirectory(FilePath* result) {
206 return base::mac::GetLocalDirectory(NSLibraryDirectory, result);
209 bool GetGlobalApplicationSupportDirectory(FilePath* result) {
210 return base::mac::GetLocalDirectory(NSApplicationSupportDirectory, result);
213 NSBundle* OuterAppBundle() {
214 // Cache this. Foundation leaks it anyway, and this should be the only call
215 // to OuterAppBundleInternal().
216 static NSBundle* bundle = OuterAppBundleInternal();
220 #endif // !defined(OS_IOS)
222 bool ProcessNeedsProfileDir(const std::string& process_type) {
223 // For now we have no reason to forbid this on other MacOS as we don't
224 // have the roaming profile troubles there.
228 } // namespace chrome