• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (c) 2011 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/mac_util.h"
16#import "base/mac/scoped_nsautorelease_pool.h"
17#include "base/path_service.h"
18#include "chrome/common/chrome_constants.h"
19
20namespace {
21
22const FilePath* g_override_versioned_directory = NULL;
23
24NSBundle* OuterAppBundle() {
25  if (!base::mac::AmIBundled()) {
26    // If unbundled (as in a test), there's no app bundle.
27    return nil;
28  }
29
30  if (!base::mac::IsBackgroundOnlyProcess()) {
31    // Shortcut: in the browser process, just return the main app bundle.
32    return [NSBundle mainBundle];
33  }
34
35  // From C.app/Contents/Versions/1.2.3.4, go up three steps to get to C.app.
36  FilePath versioned_dir = chrome::GetVersionedDirectory();
37  FilePath outer_app_dir = versioned_dir.DirName().DirName().DirName();
38  const char* outer_app_dir_c = outer_app_dir.value().c_str();
39  NSString* outer_app_dir_ns = [NSString stringWithUTF8String:outer_app_dir_c];
40
41  return [NSBundle bundleWithPath:outer_app_dir_ns];
42}
43
44const char* ProductDirNameInternal() {
45  base::mac::ScopedNSAutoreleasePool pool;
46
47  // Use OuterAppBundle() to get the main app's bundle. This key needs to live
48  // in the main app's bundle because it will be set differently on the canary
49  // channel, and the autoupdate system dictates that there can be no
50  // differences between channels within the versioned directory. This would
51  // normally use base::mac::MainAppBundle(), but that references the
52  // framework bundle within the versioned directory. Ordinarily, the profile
53  // should not be accessed from non-browser processes, but those processes do
54  // attempt to get the profile directory, so direct them to look in the outer
55  // browser .app's Info.plist for the CrProductDirName key.
56  NSBundle* bundle = OuterAppBundle();
57  NSString* product_dir_name_ns =
58      [bundle objectForInfoDictionaryKey:@"CrProductDirName"];
59  const char* product_dir_name = [product_dir_name_ns fileSystemRepresentation];
60
61  if (!product_dir_name) {
62#if defined(GOOGLE_CHROME_BUILD)
63    product_dir_name = "Google/Chrome";
64#else
65    product_dir_name = "Chromium";
66#endif
67  }
68
69  // Leaked, but the only caller initializes a static with this result, so it
70  // only happens once, and that's OK.
71  return strdup(product_dir_name);
72}
73
74// ProductDirName returns the name of the directory inside
75// ~/Library/Application Support that should hold the product application
76// data. This can be overridden by setting the CrProductDirName key in the
77// outer browser .app's Info.plist. The default is "Google/Chrome" for
78// officially-branded builds, and "Chromium" for unbranded builds. For the
79// official canary channel, the Info.plist will have CrProductDirName set
80// to "Google/Chrome Canary".
81std::string ProductDirName() {
82  static const char* product_dir_name = ProductDirNameInternal();
83  return std::string(product_dir_name);
84}
85
86}  // namespace
87
88namespace chrome {
89
90bool GetDefaultUserDataDirectory(FilePath* result) {
91  bool success = false;
92  if (result && PathService::Get(base::DIR_APP_DATA, result)) {
93    *result = result->Append(ProductDirName());
94    success = true;
95  }
96  return success;
97}
98
99bool GetUserDocumentsDirectory(FilePath* result) {
100  return base::mac::GetUserDirectory(NSDocumentDirectory, result);
101}
102
103void GetUserCacheDirectory(const FilePath& profile_dir, FilePath* result) {
104  // If the profile directory is under ~/Library/Application Support,
105  // use a suitable cache directory under ~/Library/Caches.  For
106  // example, a profile directory of ~/Library/Application
107  // Support/Google/Chrome/MyProfileName would use the cache directory
108  // ~/Library/Caches/Google/Chrome/MyProfileName.
109
110  // Default value in cases where any of the following fails.
111  *result = profile_dir;
112
113  FilePath app_data_dir;
114  if (!PathService::Get(base::DIR_APP_DATA, &app_data_dir))
115    return;
116  FilePath cache_dir;
117  if (!PathService::Get(base::DIR_CACHE, &cache_dir))
118    return;
119  if (!app_data_dir.AppendRelativePath(profile_dir, &cache_dir))
120    return;
121
122  *result = cache_dir;
123}
124
125bool GetUserDownloadsDirectory(FilePath* result) {
126  return base::mac::GetUserDirectory(NSDownloadsDirectory, result);
127}
128
129bool GetUserDesktop(FilePath* result) {
130  return base::mac::GetUserDirectory(NSDesktopDirectory, result);
131}
132
133FilePath GetVersionedDirectory() {
134  if (g_override_versioned_directory)
135    return *g_override_versioned_directory;
136
137  // Start out with the path to the running executable.
138  FilePath path;
139  PathService::Get(base::FILE_EXE, &path);
140
141  // One step up to MacOS, another to Contents.
142  path = path.DirName().DirName();
143  DCHECK_EQ(path.BaseName().value(), "Contents");
144
145  if (base::mac::IsBackgroundOnlyProcess()) {
146    // path identifies the helper .app's Contents directory in the browser
147    // .app's versioned directory.  Go up two steps to get to the browser
148    // .app's versioned directory.
149    path = path.DirName().DirName();
150    DCHECK_EQ(path.BaseName().value(), kChromeVersion);
151  } else {
152    // Go into the versioned directory.
153    path = path.Append("Versions").Append(kChromeVersion);
154  }
155
156  return path;
157}
158
159void SetOverrideVersionedDirectory(const FilePath* path) {
160  if (path != g_override_versioned_directory) {
161    delete g_override_versioned_directory;
162    g_override_versioned_directory = path;
163  }
164}
165
166FilePath GetFrameworkBundlePath() {
167  // It's tempting to use +[NSBundle bundleWithIdentifier:], but it's really
168  // slow (about 30ms on 10.5 and 10.6), despite Apple's documentation stating
169  // that it may be more efficient than +bundleForClass:.  +bundleForClass:
170  // itself takes 1-2ms.  Getting an NSBundle from a path, on the other hand,
171  // essentially takes no time at all, at least when the bundle has already
172  // been loaded as it will have been in this case.  The FilePath operations
173  // needed to compute the framework's path are also effectively free, so that
174  // is the approach that is used here.  NSBundle is also documented as being
175  // not thread-safe, and thread safety may be a concern here.
176
177  // The framework bundle is at a known path and name from the browser .app's
178  // versioned directory.
179  return GetVersionedDirectory().Append(kFrameworkName);
180}
181
182bool GetLocalLibraryDirectory(FilePath* result) {
183  return base::mac::GetLocalDirectory(NSLibraryDirectory, result);
184}
185
186}  // namespace chrome
187