Extract SIGPIPE ignoring code to a common place.
[chromium-blink-merge.git] / chrome / common / chrome_paths_mac.mm
blob54729039d917ff920e36ca8a521efcb2e8d2ba23
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>
8 #include <string.h>
10 #include <string>
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"
19 #if !defined(OS_IOS)
20 #import "base/mac/mac_util.h"
21 #endif
23 namespace {
25 #if !defined(OS_IOS)
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
30 // caller.
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.
36     return nil;
37   }
39   if (!base::mac::IsBackgroundOnlyProcess()) {
40     // Shortcut: in the browser process, just return the main app bundle.
41     return [[NSBundle mainBundle] retain];
42   }
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;
56 #if !defined(OS_IOS)
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];
72 #endif
74   if (!product_dir_name) {
75 #if defined(GOOGLE_CHROME_BUILD)
76     product_dir_name = "Google/Chrome";
77 #else
78     product_dir_name = "Chromium";
79 #endif
80   }
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);
99 }  // namespace
101 namespace chrome {
103 bool GetDefaultUserDataDirectory(FilePath* result) {
104   bool success = false;
105   if (result && PathService::Get(base::DIR_APP_DATA, result)) {
106     *result = result->Append(ProductDirName());
107     success = true;
108   }
109   return success;
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))
128     return;
129   FilePath cache_dir;
130   if (!PathService::Get(base::DIR_CACHE, &cache_dir))
131     return;
132   if (!app_data_dir.AppendRelativePath(profile_dir, &cache_dir))
133     return;
135   *result = 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);
154 #if !defined(OS_IOS)
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.
161   FilePath path;
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);
174   } else {
175     // Go into the versioned directory.
176     path = path.Append("Versions").Append(kChromeVersion);
177   }
179   return path;
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;
186   }
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();
217   return bundle;
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.
225   return true;
228 }  // namespace chrome