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 247 248 249 250 251 252 253 254 255 256
|
// 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/scoped_ptr.h"
#include "base/path_service.h"
#include "chrome/common/chrome_constants.h"
#if !defined(OS_IOS)
#import "base/mac/mac_util.h"
#endif
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 GetUserLibraryDirectory(base::FilePath* result) {
return base::mac::GetUserDirectory(NSLibraryDirectory, result);
}
bool GetUserApplicationsDirectory(base::FilePath* result) {
return base::mac::GetUserDirectory(NSApplicationDirectory, 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
|