summaryrefslogtreecommitdiffstats
path: root/chrome/common/chrome_paths_mac.mm
blob: db3262927558e4eaa5ad72c03028e74209935f63 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/common/chrome_paths_internal.h"

#import <Foundation/Foundation.h>
#include <string.h>

#include <string>

#include "base/base_paths.h"
#include "base/logging.h"
#import "base/mac/foundation_util.h"
#import "base/mac/scoped_nsautorelease_pool.h"
#include "base/memory/free_deleter.h"
#include "base/memory/scoped_ptr.h"
#include "base/path_service.h"
#include "build/build_config.h"
#include "chrome/common/chrome_constants.h"

namespace {

#if !defined(OS_IOS)
const base::FilePath* g_override_versioned_directory = NULL;

// Return a retained (NOT autoreleased) NSBundle* as the internal
// implementation of chrome::OuterAppBundle(), which should be the only
// caller.
NSBundle* OuterAppBundleInternal() {
  base::mac::ScopedNSAutoreleasePool pool;

  if (!base::mac::AmIBundled()) {
    // If unbundled (as in a test), there's no app bundle.
    return nil;
  }

  if (!base::mac::IsBackgroundOnlyProcess()) {
    // Shortcut: in the browser process, just return the main app bundle.
    return [[NSBundle mainBundle] retain];
  }

  // From C.app/Contents/Versions/1.2.3.4, go up three steps to get to C.app.
  base::FilePath versioned_dir = chrome::GetVersionedDirectory();
  base::FilePath outer_app_dir = versioned_dir.DirName().DirName().DirName();
  const char* outer_app_dir_c = outer_app_dir.value().c_str();
  NSString* outer_app_dir_ns = [NSString stringWithUTF8String:outer_app_dir_c];

  return [[NSBundle bundleWithPath:outer_app_dir_ns] retain];
}
#endif  // !defined(OS_IOS)

char* ProductDirNameForBundle(NSBundle* chrome_bundle) {
  const char* product_dir_name = NULL;
#if !defined(OS_IOS)
  base::mac::ScopedNSAutoreleasePool pool;

  NSString* product_dir_name_ns =
      [chrome_bundle objectForInfoDictionaryKey:@"CrProductDirName"];
  product_dir_name = [product_dir_name_ns fileSystemRepresentation];
#else
  DCHECK(!chrome_bundle);
#endif

  if (!product_dir_name) {
#if defined(GOOGLE_CHROME_BUILD)
    product_dir_name = "Google/Chrome";
#else
    product_dir_name = "Chromium";
#endif
  }

  // Leaked, but the only caller initializes a static with this result, so it
  // only happens once, and that's OK.
  return strdup(product_dir_name);
}

// ProductDirName returns the name of the directory inside
// ~/Library/Application Support that should hold the product application
// data. This can be overridden by setting the CrProductDirName key in the
// outer browser .app's Info.plist. The default is "Google/Chrome" for
// officially-branded builds, and "Chromium" for unbranded builds. For the
// official canary channel, the Info.plist will have CrProductDirName set
// to "Google/Chrome Canary".
std::string ProductDirName() {
#if defined(OS_IOS)
  static const char* product_dir_name = ProductDirNameForBundle(nil);
#else
  // Use OuterAppBundle() to get the main app's bundle. This key needs to live
  // in the main app's bundle because it will be set differently on the canary
  // channel, and the autoupdate system dictates that there can be no
  // differences between channels within the versioned directory. This would
  // normally use base::mac::FrameworkBundle(), but that references the
  // framework bundle within the versioned directory. Ordinarily, the profile
  // should not be accessed from non-browser processes, but those processes do
  // attempt to get the profile directory, so direct them to look in the outer
  // browser .app's Info.plist for the CrProductDirName key.
  static const char* product_dir_name =
      ProductDirNameForBundle(chrome::OuterAppBundle());
#endif
  return std::string(product_dir_name);
}

bool GetDefaultUserDataDirectoryForProduct(const std::string& product_dir,
                                           base::FilePath* result) {
  bool success = false;
  if (result && PathService::Get(base::DIR_APP_DATA, result)) {
    *result = result->Append(product_dir);
    success = true;
  }
  return success;
}

}  // namespace

namespace chrome {

bool GetDefaultUserDataDirectory(base::FilePath* result) {
  return GetDefaultUserDataDirectoryForProduct(ProductDirName(), result);
}

bool GetUserDocumentsDirectory(base::FilePath* result) {
  return base::mac::GetUserDirectory(NSDocumentDirectory, result);
}

void GetUserCacheDirectory(const base::FilePath& profile_dir,
                           base::FilePath* result) {
  // If the profile directory is under ~/Library/Application Support,
  // use a suitable cache directory under ~/Library/Caches.  For
  // example, a profile directory of ~/Library/Application
  // Support/Google/Chrome/MyProfileName would use the cache directory
  // ~/Library/Caches/Google/Chrome/MyProfileName.

  // Default value in cases where any of the following fails.
  *result = profile_dir;

  base::FilePath app_data_dir;
  if (!PathService::Get(base::DIR_APP_DATA, &app_data_dir))
    return;
  base::FilePath cache_dir;
  if (!PathService::Get(base::DIR_CACHE, &cache_dir))
    return;
  if (!app_data_dir.AppendRelativePath(profile_dir, &cache_dir))
    return;

  *result = cache_dir;
}

bool GetUserDownloadsDirectory(base::FilePath* result) {
  return base::mac::GetUserDirectory(NSDownloadsDirectory, result);
}

bool GetUserMusicDirectory(base::FilePath* result) {
  return base::mac::GetUserDirectory(NSMusicDirectory, result);
}

bool GetUserPicturesDirectory(base::FilePath* result) {
  return base::mac::GetUserDirectory(NSPicturesDirectory, result);
}

bool GetUserVideosDirectory(base::FilePath* result) {
  return base::mac::GetUserDirectory(NSMoviesDirectory, result);
}

#if !defined(OS_IOS)

base::FilePath GetVersionedDirectory() {
  if (g_override_versioned_directory)
    return *g_override_versioned_directory;

  // Start out with the path to the running executable.
  base::FilePath path;
  PathService::Get(base::FILE_EXE, &path);

  // One step up to MacOS, another to Contents.
  path = path.DirName().DirName();
  DCHECK_EQ(path.BaseName().value(), "Contents");

  if (base::mac::IsBackgroundOnlyProcess()) {
    // path identifies the helper .app's Contents directory in the browser
    // .app's versioned directory.  Go up two steps to get to the browser
    // .app's versioned directory.
    path = path.DirName().DirName();
    DCHECK_EQ(path.BaseName().value(), kChromeVersion);
  } else {
    // Go into the versioned directory.
    path = path.Append("Versions").Append(kChromeVersion);
  }

  return path;
}

void SetOverrideVersionedDirectory(const base::FilePath* path) {
  if (path != g_override_versioned_directory) {
    delete g_override_versioned_directory;
    g_override_versioned_directory = path;
  }
}

base::FilePath GetFrameworkBundlePath() {
  // It's tempting to use +[NSBundle bundleWithIdentifier:], but it's really
  // slow (about 30ms on 10.5 and 10.6), despite Apple's documentation stating
  // that it may be more efficient than +bundleForClass:.  +bundleForClass:
  // itself takes 1-2ms.  Getting an NSBundle from a path, on the other hand,
  // essentially takes no time at all, at least when the bundle has already
  // been loaded as it will have been in this case.  The FilePath operations
  // needed to compute the framework's path are also effectively free, so that
  // is the approach that is used here.  NSBundle is also documented as being
  // not thread-safe, and thread safety may be a concern here.

  // The framework bundle is at a known path and name from the browser .app's
  // versioned directory.
  return GetVersionedDirectory().Append(kFrameworkName);
}

bool GetLocalLibraryDirectory(base::FilePath* result) {
  return base::mac::GetLocalDirectory(NSLibraryDirectory, result);
}

bool GetGlobalApplicationSupportDirectory(base::FilePath* result) {
  return base::mac::GetLocalDirectory(NSApplicationSupportDirectory, result);
}

NSBundle* OuterAppBundle() {
  // Cache this. Foundation leaks it anyway, and this should be the only call
  // to OuterAppBundleInternal().
  static NSBundle* bundle = OuterAppBundleInternal();
  return bundle;
}

bool GetUserDataDirectoryForBrowserBundle(NSBundle* bundle,
                                          base::FilePath* result) {
  scoped_ptr<char, base::FreeDeleter>
      product_dir_name(ProductDirNameForBundle(bundle));
  return GetDefaultUserDataDirectoryForProduct(product_dir_name.get(), result);
}

#endif  // !defined(OS_IOS)

bool ProcessNeedsProfileDir(const std::string& process_type) {
  // For now we have no reason to forbid this on other MacOS as we don't
  // have the roaming profile troubles there.
  return true;
}

}  // namespace chrome