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