Avi Drissman | 4a8573c | 2022-09-09 19:35:54 | [diff] [blame] | 1 | // Copyright 2012 The Chromium Authors |
pinkerton@chromium.org | 409993dec | 2009-03-03 20:23:04 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
mark@chromium.org | 927e90a | 2011-04-19 02:31:42 | [diff] [blame] | 5 | #import <Foundation/Foundation.h> |
mark@chromium.org | 1ce9fe7 | 2011-04-19 17:33:00 | [diff] [blame] | 6 | #include <string.h> |
| 7 | |
thakis | 8b51c0a | 2016-04-12 19:48:18 | [diff] [blame] | 8 | #include <memory> |
mark@chromium.org | 1ce9fe7 | 2011-04-19 17:33:00 | [diff] [blame] | 9 | #include <string> |
pinkerton@chromium.org | 409993dec | 2009-03-03 20:23:04 | [diff] [blame] | 10 | |
mark@chromium.org | e4221ad5 | 2009-10-07 16:42:16 | [diff] [blame] | 11 | #include "base/base_paths.h" |
Hans Wennborg | a47ddf8 | 2020-05-05 18:08:07 | [diff] [blame] | 12 | #include "base/check_op.h" |
mark@chromium.org | 927e90a | 2011-04-19 02:31:42 | [diff] [blame] | 13 | #import "base/mac/foundation_util.h" |
dcheng | db5935f | 2016-03-26 00:16:27 | [diff] [blame] | 14 | #include "base/memory/free_deleter.h" |
mark@chromium.org | e4221ad5 | 2009-10-07 16:42:16 | [diff] [blame] | 15 | #include "base/path_service.h" |
Leonard Grey | ab27aae2 | 2021-05-07 14:44:06 | [diff] [blame] | 16 | #include "base/strings/sys_string_conversions.h" |
Nico Weber | 9116a2f1 | 2019-10-07 16:32:12 | [diff] [blame] | 17 | #include "build/branding_buildflags.h" |
avi | 2729e44 | 2015-12-26 05:27:45 | [diff] [blame] | 18 | #include "build/build_config.h" |
mark@chromium.org | 6a271dba | 2009-10-08 20:03:54 | [diff] [blame] | 19 | #include "chrome/common/chrome_constants.h" |
thakis | 8b51c0a | 2016-04-12 19:48:18 | [diff] [blame] | 20 | #include "chrome/common/chrome_paths_internal.h" |
pinkerton@chromium.org | 409993dec | 2009-03-03 20:23:04 | [diff] [blame] | 21 | |
viettrungluu@chromium.org | adf0225 | 2010-05-18 22:09:40 | [diff] [blame] | 22 | namespace { |
mark@chromium.org | 927e90a | 2011-04-19 02:31:42 | [diff] [blame] | 23 | |
Avi Drissman | 3f5ffc1 | 2023-05-08 21:23:46 | [diff] [blame] | 24 | // Return an NSBundle* as the internal implementation of |
| 25 | // chrome::OuterAppBundle(), which should be the only caller. |
mark@chromium.org | 151c4a6 | 2011-04-22 04:15:13 | [diff] [blame] | 26 | NSBundle* OuterAppBundleInternal() { |
Avi Drissman | 91160c63 | 2019-09-06 19:24:58 | [diff] [blame] | 27 | @autoreleasepool { |
| 28 | if (!base::mac::AmIBundled()) { |
| 29 | // If unbundled (as in a test), there's no app bundle. |
| 30 | return nil; |
| 31 | } |
mark@chromium.org | 254a99c5 | 2011-04-25 17:57:23 | [diff] [blame] | 32 | |
Avi Drissman | 91160c63 | 2019-09-06 19:24:58 | [diff] [blame] | 33 | if (!base::mac::IsBackgroundOnlyProcess()) { |
| 34 | // Shortcut: in the browser process, just return the main app bundle. |
Avi Drissman | 3f5ffc1 | 2023-05-08 21:23:46 | [diff] [blame] | 35 | return NSBundle.mainBundle; |
Avi Drissman | 91160c63 | 2019-09-06 19:24:58 | [diff] [blame] | 36 | } |
| 37 | |
| 38 | // From C.app/Contents/Frameworks/C.framework/Versions/1.2.3.4, go up five |
| 39 | // steps to C.app. |
| 40 | base::FilePath framework_path = chrome::GetFrameworkBundlePath(); |
| 41 | base::FilePath outer_app_dir = |
| 42 | framework_path.DirName().DirName().DirName().DirName().DirName(); |
Leonard Grey | ab27aae2 | 2021-05-07 14:44:06 | [diff] [blame] | 43 | NSString* outer_app_dir_ns = base::SysUTF8ToNSString(outer_app_dir.value()); |
Avi Drissman | 91160c63 | 2019-09-06 19:24:58 | [diff] [blame] | 44 | |
Avi Drissman | 3f5ffc1 | 2023-05-08 21:23:46 | [diff] [blame] | 45 | return [NSBundle bundleWithPath:outer_app_dir_ns]; |
mark@chromium.org | 927e90a | 2011-04-19 02:31:42 | [diff] [blame] | 46 | } |
mark@chromium.org | 927e90a | 2011-04-19 02:31:42 | [diff] [blame] | 47 | } |
| 48 | |
jeremya@chromium.org | 7944d248 | 2013-04-05 23:27:21 | [diff] [blame] | 49 | char* ProductDirNameForBundle(NSBundle* chrome_bundle) { |
Avi Drissman | 91160c63 | 2019-09-06 19:24:58 | [diff] [blame] | 50 | @autoreleasepool { |
Avi Drissman | 3f5ffc1 | 2023-05-08 21:23:46 | [diff] [blame] | 51 | const char* product_dir_name = nullptr; |
mark@chromium.org | 1ce9fe7 | 2011-04-19 17:33:00 | [diff] [blame] | 52 | |
Avi Drissman | 91160c63 | 2019-09-06 19:24:58 | [diff] [blame] | 53 | NSString* product_dir_name_ns = |
| 54 | [chrome_bundle objectForInfoDictionaryKey:@"CrProductDirName"]; |
| 55 | product_dir_name = [product_dir_name_ns fileSystemRepresentation]; |
mark@chromium.org | 927e90a | 2011-04-19 02:31:42 | [diff] [blame] | 56 | |
Avi Drissman | 91160c63 | 2019-09-06 19:24:58 | [diff] [blame] | 57 | if (!product_dir_name) { |
Nico Weber | 9116a2f1 | 2019-10-07 16:32:12 | [diff] [blame] | 58 | #if BUILDFLAG(GOOGLE_CHROME_BRANDING) |
Avi Drissman | 91160c63 | 2019-09-06 19:24:58 | [diff] [blame] | 59 | product_dir_name = "Google/Chrome"; |
mark@chromium.org | 927e90a | 2011-04-19 02:31:42 | [diff] [blame] | 60 | #else |
Avi Drissman | 91160c63 | 2019-09-06 19:24:58 | [diff] [blame] | 61 | product_dir_name = "Chromium"; |
mark@chromium.org | 927e90a | 2011-04-19 02:31:42 | [diff] [blame] | 62 | #endif |
Avi Drissman | 91160c63 | 2019-09-06 19:24:58 | [diff] [blame] | 63 | } |
mark@chromium.org | 927e90a | 2011-04-19 02:31:42 | [diff] [blame] | 64 | |
Avi Drissman | 91160c63 | 2019-09-06 19:24:58 | [diff] [blame] | 65 | // Leaked, but the only caller initializes a static with this result, so it |
| 66 | // only happens once, and that's OK. |
| 67 | return strdup(product_dir_name); |
| 68 | } |
mark@chromium.org | 927e90a | 2011-04-19 02:31:42 | [diff] [blame] | 69 | } |
| 70 | |
| 71 | // ProductDirName returns the name of the directory inside |
| 72 | // ~/Library/Application Support that should hold the product application |
| 73 | // data. This can be overridden by setting the CrProductDirName key in the |
| 74 | // outer browser .app's Info.plist. The default is "Google/Chrome" for |
| 75 | // officially-branded builds, and "Chromium" for unbranded builds. For the |
| 76 | // official canary channel, the Info.plist will have CrProductDirName set |
| 77 | // to "Google/Chrome Canary". |
| 78 | std::string ProductDirName() { |
jeremya@chromium.org | 7944d248 | 2013-04-05 23:27:21 | [diff] [blame] | 79 | // Use OuterAppBundle() to get the main app's bundle. This key needs to live |
| 80 | // in the main app's bundle because it will be set differently on the canary |
| 81 | // channel, and the autoupdate system dictates that there can be no |
| 82 | // differences between channels within the versioned directory. This would |
Avi Drissman | d4f0708 | 2023-05-12 18:05:44 | [diff] [blame] | 83 | // normally use base::apple::FrameworkBundle(), but that references the |
jeremya@chromium.org | 7944d248 | 2013-04-05 23:27:21 | [diff] [blame] | 84 | // framework bundle within the versioned directory. Ordinarily, the profile |
| 85 | // should not be accessed from non-browser processes, but those processes do |
| 86 | // attempt to get the profile directory, so direct them to look in the outer |
| 87 | // browser .app's Info.plist for the CrProductDirName key. |
| 88 | static const char* product_dir_name = |
| 89 | ProductDirNameForBundle(chrome::OuterAppBundle()); |
mark@chromium.org | 927e90a | 2011-04-19 02:31:42 | [diff] [blame] | 90 | return std::string(product_dir_name); |
| 91 | } |
| 92 | |
jeremya@chromium.org | 7944d248 | 2013-04-05 23:27:21 | [diff] [blame] | 93 | bool GetDefaultUserDataDirectoryForProduct(const std::string& product_dir, |
| 94 | base::FilePath* result) { |
| 95 | bool success = false; |
Avi Drissman | 7d43b4b | 2018-05-01 16:14:06 | [diff] [blame] | 96 | if (result && base::PathService::Get(base::DIR_APP_DATA, result)) { |
jeremya@chromium.org | 7944d248 | 2013-04-05 23:27:21 | [diff] [blame] | 97 | *result = result->Append(product_dir); |
| 98 | success = true; |
| 99 | } |
| 100 | return success; |
| 101 | } |
| 102 | |
viettrungluu@chromium.org | adf0225 | 2010-05-18 22:09:40 | [diff] [blame] | 103 | } // namespace |
| 104 | |
pinkerton@chromium.org | 409993dec | 2009-03-03 20:23:04 | [diff] [blame] | 105 | namespace chrome { |
| 106 | |
brettw@chromium.org | a732916 | 2013-02-07 19:21:48 | [diff] [blame] | 107 | bool GetDefaultUserDataDirectory(base::FilePath* result) { |
jeremya@chromium.org | 7944d248 | 2013-04-05 23:27:21 | [diff] [blame] | 108 | return GetDefaultUserDataDirectoryForProduct(ProductDirName(), result); |
robertshield@chromium.org | fd1e44c | 2009-10-28 00:25:34 | [diff] [blame] | 109 | } |
| 110 | |
brettw@chromium.org | a732916 | 2013-02-07 19:21:48 | [diff] [blame] | 111 | bool GetUserDocumentsDirectory(base::FilePath* result) { |
brettw@chromium.org | 0378bf4 | 2011-01-01 18:20:14 | [diff] [blame] | 112 | return base::mac::GetUserDirectory(NSDocumentDirectory, result); |
pinkerton@chromium.org | 409993dec | 2009-03-03 20:23:04 | [diff] [blame] | 113 | } |
| 114 | |
brettw@chromium.org | a732916 | 2013-02-07 19:21:48 | [diff] [blame] | 115 | void GetUserCacheDirectory(const base::FilePath& profile_dir, |
| 116 | base::FilePath* result) { |
viettrungluu@chromium.org | b411da3 | 2010-11-24 02:23:15 | [diff] [blame] | 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. |
| 122 | |
| 123 | // Default value in cases where any of the following fails. |
| 124 | *result = profile_dir; |
| 125 | |
brettw@chromium.org | a732916 | 2013-02-07 19:21:48 | [diff] [blame] | 126 | base::FilePath app_data_dir; |
Avi Drissman | 7d43b4b | 2018-05-01 16:14:06 | [diff] [blame] | 127 | if (!base::PathService::Get(base::DIR_APP_DATA, &app_data_dir)) |
viettrungluu@chromium.org | b411da3 | 2010-11-24 02:23:15 | [diff] [blame] | 128 | return; |
brettw@chromium.org | a732916 | 2013-02-07 19:21:48 | [diff] [blame] | 129 | base::FilePath cache_dir; |
Avi Drissman | 7d43b4b | 2018-05-01 16:14:06 | [diff] [blame] | 130 | if (!base::PathService::Get(base::DIR_CACHE, &cache_dir)) |
viettrungluu@chromium.org | b411da3 | 2010-11-24 02:23:15 | [diff] [blame] | 131 | return; |
| 132 | if (!app_data_dir.AppendRelativePath(profile_dir, &cache_dir)) |
| 133 | return; |
| 134 | |
| 135 | *result = cache_dir; |
| 136 | } |
| 137 | |
brettw@chromium.org | a732916 | 2013-02-07 19:21:48 | [diff] [blame] | 138 | bool GetUserDownloadsDirectory(base::FilePath* result) { |
brettw@chromium.org | 0378bf4 | 2011-01-01 18:20:14 | [diff] [blame] | 139 | return base::mac::GetUserDirectory(NSDownloadsDirectory, result); |
pinkerton@chromium.org | 409993dec | 2009-03-03 20:23:04 | [diff] [blame] | 140 | } |
| 141 | |
brettw@chromium.org | a732916 | 2013-02-07 19:21:48 | [diff] [blame] | 142 | bool GetUserMusicDirectory(base::FilePath* result) { |
thestig@chromium.org | fa9ac63 | 2012-09-14 04:18:14 | [diff] [blame] | 143 | return base::mac::GetUserDirectory(NSMusicDirectory, result); |
| 144 | } |
| 145 | |
brettw@chromium.org | a732916 | 2013-02-07 19:21:48 | [diff] [blame] | 146 | bool GetUserPicturesDirectory(base::FilePath* result) { |
thestig@chromium.org | a87c920b | 2012-05-17 01:06:57 | [diff] [blame] | 147 | return base::mac::GetUserDirectory(NSPicturesDirectory, result); |
| 148 | } |
| 149 | |
brettw@chromium.org | a732916 | 2013-02-07 19:21:48 | [diff] [blame] | 150 | bool GetUserVideosDirectory(base::FilePath* result) { |
thestig@chromium.org | fa9ac63 | 2012-09-14 04:18:14 | [diff] [blame] | 151 | return base::mac::GetUserDirectory(NSMoviesDirectory, result); |
| 152 | } |
| 153 | |
brettw@chromium.org | a732916 | 2013-02-07 19:21:48 | [diff] [blame] | 154 | base::FilePath GetFrameworkBundlePath() { |
mark@chromium.org | da90173f | 2009-10-07 22:57:09 | [diff] [blame] | 155 | // It's tempting to use +[NSBundle bundleWithIdentifier:], but it's really |
| 156 | // slow (about 30ms on 10.5 and 10.6), despite Apple's documentation stating |
| 157 | // that it may be more efficient than +bundleForClass:. +bundleForClass: |
| 158 | // itself takes 1-2ms. Getting an NSBundle from a path, on the other hand, |
| 159 | // essentially takes no time at all, at least when the bundle has already |
| 160 | // been loaded as it will have been in this case. The FilePath operations |
| 161 | // needed to compute the framework's path are also effectively free, so that |
mark@chromium.org | b88a291b | 2009-10-15 01:22:51 | [diff] [blame] | 162 | // is the approach that is used here. NSBundle is also documented as being |
| 163 | // not thread-safe, and thread safety may be a concern here. |
thomasvl@chromium.org | a2126d5e | 2009-08-24 16:43:04 | [diff] [blame] | 164 | |
Robert Sesek | 07bd6b9 | 2019-05-15 16:10:34 | [diff] [blame] | 165 | // Start out with the path to the running executable. |
| 166 | base::FilePath path; |
| 167 | base::PathService::Get(base::FILE_EXE, &path); |
| 168 | |
| 169 | // One step up to MacOS, another to Contents. |
| 170 | path = path.DirName().DirName(); |
| 171 | DCHECK_EQ(path.BaseName().value(), "Contents"); |
| 172 | |
| 173 | if (base::mac::IsBackgroundOnlyProcess()) { |
| 174 | // |path| is Chromium.app/Contents/Frameworks/Chromium Framework.framework/ |
Robert Sesek | 49937668 | 2019-05-16 23:25:48 | [diff] [blame] | 175 | // Versions/X/Helpers/Chromium Helper.app/Contents. Go up three times to |
| 176 | // the versioned framework directory. |
| 177 | path = path.DirName().DirName().DirName(); |
Robert Sesek | 07bd6b9 | 2019-05-15 16:10:34 | [diff] [blame] | 178 | } else { |
| 179 | // |path| is Chromium.app/Contents, so go down to |
Robert Sesek | 49937668 | 2019-05-16 23:25:48 | [diff] [blame] | 180 | // Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/X. |
| 181 | path = path.Append("Frameworks") |
| 182 | .Append(kFrameworkName) |
| 183 | .Append("Versions") |
| 184 | .Append(kChromeVersion); |
Robert Sesek | 07bd6b9 | 2019-05-15 16:10:34 | [diff] [blame] | 185 | } |
Robert Sesek | 49937668 | 2019-05-16 23:25:48 | [diff] [blame] | 186 | DCHECK_EQ(path.BaseName().value(), kChromeVersion); |
| 187 | DCHECK_EQ(path.DirName().BaseName().value(), "Versions"); |
| 188 | DCHECK_EQ(path.DirName().DirName().BaseName().value(), kFrameworkName); |
| 189 | DCHECK_EQ(path.DirName().DirName().DirName().BaseName().value(), |
| 190 | "Frameworks"); |
| 191 | DCHECK_EQ(path.DirName() |
| 192 | .DirName() |
| 193 | .DirName() |
| 194 | .DirName() |
| 195 | .DirName() |
| 196 | .BaseName() |
| 197 | .Extension(), |
| 198 | ".app"); |
Robert Sesek | 07bd6b9 | 2019-05-15 16:10:34 | [diff] [blame] | 199 | return path; |
thomasvl@chromium.org | a2126d5e | 2009-08-24 16:43:04 | [diff] [blame] | 200 | } |
| 201 | |
brettw@chromium.org | a732916 | 2013-02-07 19:21:48 | [diff] [blame] | 202 | bool GetLocalLibraryDirectory(base::FilePath* result) { |
brettw@chromium.org | 0378bf4 | 2011-01-01 18:20:14 | [diff] [blame] | 203 | return base::mac::GetLocalDirectory(NSLibraryDirectory, result); |
mnissler@chromium.org | 7c59723 | 2010-10-29 13:09:55 | [diff] [blame] | 204 | } |
| 205 | |
brettw@chromium.org | a732916 | 2013-02-07 19:21:48 | [diff] [blame] | 206 | bool GetGlobalApplicationSupportDirectory(base::FilePath* result) { |
stuartmorgan@chromium.org | b7d1efa6 | 2012-11-16 11:22:02 | [diff] [blame] | 207 | return base::mac::GetLocalDirectory(NSApplicationSupportDirectory, result); |
| 208 | } |
| 209 | |
mark@chromium.org | 151c4a6 | 2011-04-22 04:15:13 | [diff] [blame] | 210 | NSBundle* OuterAppBundle() { |
mark@chromium.org | 254a99c5 | 2011-04-25 17:57:23 | [diff] [blame] | 211 | static NSBundle* bundle = OuterAppBundleInternal(); |
mark@chromium.org | 151c4a6 | 2011-04-22 04:15:13 | [diff] [blame] | 212 | return bundle; |
| 213 | } |
| 214 | |
pastarmovj@chromium.org | 288e350 | 2012-05-09 12:35:03 | [diff] [blame] | 215 | bool ProcessNeedsProfileDir(const std::string& process_type) { |
Avi Drissman | 3f5ffc1 | 2023-05-08 21:23:46 | [diff] [blame] | 216 | // For now we have no reason to forbid this on other macOS as we don't |
pastarmovj@chromium.org | 288e350 | 2012-05-09 12:35:03 | [diff] [blame] | 217 | // have the roaming profile troubles there. |
| 218 | return true; |
| 219 | } |
| 220 | |
pinkerton@chromium.org | 409993dec | 2009-03-03 20:23:04 | [diff] [blame] | 221 | } // namespace chrome |