blob: a3b882c2a9e8f76f668ed71b8549706059aecf51 [file] [log] [blame]
pastarmovj@chromium.org8f452092012-05-09 19:50:10 +09001// Copyright (c) 2012 The Chromium Authors. All rights reserved.
license.botf003cfe2008-08-24 09:55:55 +09002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit3f4a7322008-07-27 06:49:38 +09004
maruel@google.com15cfc7f2008-08-08 05:23:09 +09005#include "base/path_service.h"
avi@google.comac68ba22008-08-07 01:01:25 +09006
7#ifdef OS_WIN
initial.commit3f4a7322008-07-27 06:49:38 +09008#include <windows.h>
9#include <shellapi.h>
10#include <shlobj.h>
avi@google.comac68ba22008-08-07 01:01:25 +090011#endif
initial.commit3f4a7322008-07-27 06:49:38 +090012
evanm@google.com874d1672008-10-31 08:54:04 +090013#include "base/file_path.h"
maruel@google.com15cfc7f2008-08-08 05:23:09 +090014#include "base/file_util.h"
evanm@google.com874d1672008-10-31 08:54:04 +090015#include "base/hash_tables.h"
satish@chromium.org2940ad12010-12-14 16:48:49 +090016#include "base/lazy_instance.h"
initial.commit3f4a7322008-07-27 06:49:38 +090017#include "base/logging.h"
brettw@chromium.orgabe477a2011-01-21 13:55:52 +090018#include "base/synchronization/lock.h"
initial.commit3f4a7322008-07-27 06:49:38 +090019
20namespace base {
evanm@google.com3e9d5d72008-11-20 01:50:03 +090021 bool PathProvider(int key, FilePath* result);
erikkay@google.com9fc57d02008-08-09 05:16:08 +090022#if defined(OS_WIN)
evanm@google.com3e9d5d72008-11-20 01:50:03 +090023 bool PathProviderWin(int key, FilePath* result);
evanm@google.come41d3b32008-08-15 10:04:11 +090024#elif defined(OS_MACOSX)
evanm@google.com3e9d5d72008-11-20 01:50:03 +090025 bool PathProviderMac(int key, FilePath* result);
michaelbai@google.com2251c622011-06-22 07:34:50 +090026#elif defined(OS_ANDROID)
27 bool PathProviderAndroid(int key, FilePath* result);
evan@chromium.orgc1365092009-11-21 10:29:00 +090028#elif defined(OS_POSIX)
29 bool PathProviderPosix(int key, FilePath* result);
erikkay@google.comc7980ee2008-08-06 04:46:31 +090030#endif
initial.commit3f4a7322008-07-27 06:49:38 +090031}
32
33namespace {
34
evanm@google.com874d1672008-10-31 08:54:04 +090035typedef base::hash_map<int, FilePath> PathMap;
initial.commit3f4a7322008-07-27 06:49:38 +090036
37// We keep a linked list of providers. In a debug build we ensure that no two
38// providers claim overlapping keys.
39struct Provider {
40 PathService::ProviderFunc func;
41 struct Provider* next;
42#ifndef NDEBUG
43 int key_start;
44 int key_end;
45#endif
erikkay@google.com564ef6d2008-08-21 00:47:39 +090046 bool is_static;
initial.commit3f4a7322008-07-27 06:49:38 +090047};
48
jhawkins@chromium.org88fbaf62012-01-28 09:34:40 +090049Provider base_provider = {
initial.commit3f4a7322008-07-27 06:49:38 +090050 base::PathProvider,
51 NULL,
52#ifndef NDEBUG
53 base::PATH_START,
erikkay@google.com564ef6d2008-08-21 00:47:39 +090054 base::PATH_END,
initial.commit3f4a7322008-07-27 06:49:38 +090055#endif
erikkay@google.com564ef6d2008-08-21 00:47:39 +090056 true
initial.commit3f4a7322008-07-27 06:49:38 +090057};
58
evan@chromium.orgc1365092009-11-21 10:29:00 +090059#if defined(OS_WIN)
jhawkins@chromium.org88fbaf62012-01-28 09:34:40 +090060Provider base_provider_win = {
erikkay@google.comc7980ee2008-08-06 04:46:31 +090061 base::PathProviderWin,
62 &base_provider,
63#ifndef NDEBUG
64 base::PATH_WIN_START,
erikkay@google.com564ef6d2008-08-21 00:47:39 +090065 base::PATH_WIN_END,
erikkay@google.comc7980ee2008-08-06 04:46:31 +090066#endif
erikkay@google.com564ef6d2008-08-21 00:47:39 +090067 true
erikkay@google.comc7980ee2008-08-06 04:46:31 +090068};
69#endif
70
evan@chromium.orgc1365092009-11-21 10:29:00 +090071#if defined(OS_MACOSX)
jhawkins@chromium.org88fbaf62012-01-28 09:34:40 +090072Provider base_provider_mac = {
erikkay@google.com9fc57d02008-08-09 05:16:08 +090073 base::PathProviderMac,
74 &base_provider,
75#ifndef NDEBUG
76 base::PATH_MAC_START,
erikkay@google.com564ef6d2008-08-21 00:47:39 +090077 base::PATH_MAC_END,
erikkay@google.com9fc57d02008-08-09 05:16:08 +090078#endif
erikkay@google.com564ef6d2008-08-21 00:47:39 +090079 true
80};
erikkay@google.com9fc57d02008-08-09 05:16:08 +090081#endif
evanm@google.come41d3b32008-08-15 10:04:11 +090082
michaelbai@google.com2251c622011-06-22 07:34:50 +090083#if defined(OS_ANDROID)
jhawkins@chromium.org88fbaf62012-01-28 09:34:40 +090084Provider base_provider_android = {
michaelbai@google.com2251c622011-06-22 07:34:50 +090085 base::PathProviderAndroid,
86 &base_provider,
87#ifndef NDEBUG
wjia@chromium.org9d594d12012-09-20 10:59:36 +090088 0,
89 0,
michaelbai@google.com2251c622011-06-22 07:34:50 +090090#endif
91 true
92};
93#endif
94
95#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
jhawkins@chromium.org88fbaf62012-01-28 09:34:40 +090096Provider base_provider_posix = {
evan@chromium.orgc1365092009-11-21 10:29:00 +090097 base::PathProviderPosix,
evanm@google.come41d3b32008-08-15 10:04:11 +090098 &base_provider,
99#ifndef NDEBUG
wjia@chromium.org9d594d12012-09-20 10:59:36 +0900100 0,
101 0,
evanm@google.come41d3b32008-08-15 10:04:11 +0900102#endif
erikkay@google.com564ef6d2008-08-21 00:47:39 +0900103 true
104};
evanm@google.come41d3b32008-08-15 10:04:11 +0900105#endif
106
107
initial.commit3f4a7322008-07-27 06:49:38 +0900108struct PathData {
brettw@chromium.orgabe477a2011-01-21 13:55:52 +0900109 base::Lock lock;
110 PathMap cache; // Cache mappings from path key to path value.
111 PathMap overrides; // Track path overrides.
initial.commit3f4a7322008-07-27 06:49:38 +0900112 Provider* providers; // Linked list of path service providers.
113
erikkay@google.comc7980ee2008-08-06 04:46:31 +0900114 PathData() {
115#if defined(OS_WIN)
116 providers = &base_provider_win;
erikkay@google.com9fc57d02008-08-09 05:16:08 +0900117#elif defined(OS_MACOSX)
118 providers = &base_provider_mac;
michaelbai@google.com2251c622011-06-22 07:34:50 +0900119#elif defined(OS_ANDROID)
120 providers = &base_provider_android;
evan@chromium.orgc1365092009-11-21 10:29:00 +0900121#elif defined(OS_POSIX)
122 providers = &base_provider_posix;
erikkay@google.comc7980ee2008-08-06 04:46:31 +0900123#endif
initial.commit3f4a7322008-07-27 06:49:38 +0900124 }
erikkay@google.com564ef6d2008-08-21 00:47:39 +0900125
126 ~PathData() {
127 Provider* p = providers;
128 while (p) {
129 Provider* next = p->next;
130 if (!p->is_static)
131 delete p;
132 p = next;
133 }
134 }
initial.commit3f4a7322008-07-27 06:49:38 +0900135};
maruel@chromium.org8fe7adc2009-03-04 00:01:12 +0900136
joth@chromium.orgb24883c2011-11-15 22:31:49 +0900137static base::LazyInstance<PathData> g_path_data = LAZY_INSTANCE_INITIALIZER;
satish@chromium.org2940ad12010-12-14 16:48:49 +0900138
erikkay@google.coma3ff2752008-08-13 02:33:52 +0900139static PathData* GetPathData() {
satish@chromium.org2940ad12010-12-14 16:48:49 +0900140 return g_path_data.Pointer();
erikkay@google.coma3ff2752008-08-13 02:33:52 +0900141}
initial.commit3f4a7322008-07-27 06:49:38 +0900142
143} // namespace
144
erikkay@google.com3620e4c2008-08-12 00:38:27 +0900145
erikkay@google.coma3ff2752008-08-13 02:33:52 +0900146// static
evanm@google.com874d1672008-10-31 08:54:04 +0900147bool PathService::GetFromCache(int key, FilePath* result) {
erikkay@google.coma3ff2752008-08-13 02:33:52 +0900148 PathData* path_data = GetPathData();
brettw@chromium.orgabe477a2011-01-21 13:55:52 +0900149 base::AutoLock scoped_lock(path_data->lock);
maruel@chromium.org8fe7adc2009-03-04 00:01:12 +0900150
erikkay@google.com3620e4c2008-08-12 00:38:27 +0900151 // check for a cached version
152 PathMap::const_iterator it = path_data->cache.find(key);
153 if (it != path_data->cache.end()) {
154 *result = it->second;
155 return true;
156 }
157 return false;
158}
159
erikkay@google.coma3ff2752008-08-13 02:33:52 +0900160// static
phajdan.jr@chromium.org26faa102010-09-10 08:49:04 +0900161bool PathService::GetFromOverrides(int key, FilePath* result) {
162 PathData* path_data = GetPathData();
brettw@chromium.orgabe477a2011-01-21 13:55:52 +0900163 base::AutoLock scoped_lock(path_data->lock);
phajdan.jr@chromium.org26faa102010-09-10 08:49:04 +0900164
jhawkins@chromium.orgda5f34c2011-12-15 03:47:26 +0900165 // check for an overridden version.
phajdan.jr@chromium.org26faa102010-09-10 08:49:04 +0900166 PathMap::const_iterator it = path_data->overrides.find(key);
167 if (it != path_data->overrides.end()) {
168 *result = it->second;
169 return true;
170 }
171 return false;
172}
173
174// static
evanm@google.com874d1672008-10-31 08:54:04 +0900175void PathService::AddToCache(int key, const FilePath& path) {
erikkay@google.coma3ff2752008-08-13 02:33:52 +0900176 PathData* path_data = GetPathData();
brettw@chromium.orgabe477a2011-01-21 13:55:52 +0900177 base::AutoLock scoped_lock(path_data->lock);
erikkay@google.com3620e4c2008-08-12 00:38:27 +0900178 // Save the computed path in our cache.
179 path_data->cache[key] = path;
180}
181
initial.commit3f4a7322008-07-27 06:49:38 +0900182// TODO(brettw): this function does not handle long paths (filename > MAX_PATH)
183// characters). This isn't supported very well by Windows right now, so it is
184// moot, but we should keep this in mind for the future.
185// static
evanm@google.com874d1672008-10-31 08:54:04 +0900186bool PathService::Get(int key, FilePath* result) {
erikkay@google.coma3ff2752008-08-13 02:33:52 +0900187 PathData* path_data = GetPathData();
initial.commit3f4a7322008-07-27 06:49:38 +0900188 DCHECK(path_data);
189 DCHECK(result);
pkasting@chromium.orgd23fe1a2011-04-01 05:34:25 +0900190 DCHECK_GE(key, base::DIR_CURRENT);
initial.commit3f4a7322008-07-27 06:49:38 +0900191
192 // special case the current directory because it can never be cached
erikkay@google.com9fc57d02008-08-09 05:16:08 +0900193 if (key == base::DIR_CURRENT)
erikkay@google.com1d4507f2008-08-07 01:29:44 +0900194 return file_util::GetCurrentDirectory(result);
initial.commit3f4a7322008-07-27 06:49:38 +0900195
rvargas@google.com2fa94812010-08-20 08:20:09 +0900196 if (GetFromCache(key, result))
initial.commit3f4a7322008-07-27 06:49:38 +0900197 return true;
estade@chromium.org234a6322009-02-28 10:49:55 +0900198
phajdan.jr@chromium.org26faa102010-09-10 08:49:04 +0900199 if (GetFromOverrides(key, result))
200 return true;
201
evanm@google.com3e9d5d72008-11-20 01:50:03 +0900202 FilePath path;
initial.commit3f4a7322008-07-27 06:49:38 +0900203
204 // search providers for the requested path
erikkay@google.com3620e4c2008-08-12 00:38:27 +0900205 // NOTE: it should be safe to iterate here without the lock
206 // since RegisterProvider always prepends.
initial.commit3f4a7322008-07-27 06:49:38 +0900207 Provider* provider = path_data->providers;
208 while (provider) {
evanm@google.com3e9d5d72008-11-20 01:50:03 +0900209 if (provider->func(key, &path))
initial.commit3f4a7322008-07-27 06:49:38 +0900210 break;
estade@chromium.org234a6322009-02-28 10:49:55 +0900211 DCHECK(path.empty()) << "provider should not have modified path";
initial.commit3f4a7322008-07-27 06:49:38 +0900212 provider = provider->next;
213 }
214
estade@chromium.org234a6322009-02-28 10:49:55 +0900215 if (path.empty())
initial.commit3f4a7322008-07-27 06:49:38 +0900216 return false;
217
erikkay@google.com3620e4c2008-08-12 00:38:27 +0900218 AddToCache(key, path);
estade@chromium.org234a6322009-02-28 10:49:55 +0900219
evanm@google.com874d1672008-10-31 08:54:04 +0900220 *result = path;
221 return true;
222}
223
jcampan@chromium.orgcce10e72009-06-26 02:29:09 +0900224bool PathService::Override(int key, const FilePath& path) {
pastarmovj@chromium.org8f452092012-05-09 19:50:10 +0900225 // Just call the full function with true for the value of |create|.
226 return OverrideAndCreateIfNeeded(key, path, true);
227}
228
229bool PathService::OverrideAndCreateIfNeeded(int key,
230 const FilePath& path,
231 bool create) {
erikkay@google.coma3ff2752008-08-13 02:33:52 +0900232 PathData* path_data = GetPathData();
initial.commit3f4a7322008-07-27 06:49:38 +0900233 DCHECK(path_data);
kushi.p@gmail.come18fdd32011-03-14 07:13:33 +0900234 DCHECK_GT(key, base::DIR_CURRENT) << "invalid path key";
initial.commit3f4a7322008-07-27 06:49:38 +0900235
jcampan@chromium.orgcce10e72009-06-26 02:29:09 +0900236 FilePath file_path = path;
initial.commit3f4a7322008-07-27 06:49:38 +0900237
pastarmovj@chromium.org8f452092012-05-09 19:50:10 +0900238 // For some locations this will fail if called from inside the sandbox there-
239 // fore we protect this call with a flag.
240 if (create) {
241 // Make sure the directory exists. We need to do this before we translate
242 // this to the absolute path because on POSIX, AbsolutePath fails if called
243 // on a non-existent path.
244 if (!file_util::PathExists(file_path) &&
245 !file_util::CreateDirectory(file_path))
246 return false;
247 }
initial.commit3f4a7322008-07-27 06:49:38 +0900248
erg@google.com08945ec2009-10-28 08:31:36 +0900249 // We need to have an absolute path, as extensions and plugins don't like
jhawkins@chromium.orgda5f34c2011-12-15 03:47:26 +0900250 // relative paths, and will gladly crash the browser in CHECK()s if they get a
erg@google.com08945ec2009-10-28 08:31:36 +0900251 // relative path.
252 if (!file_util::AbsolutePath(&file_path))
253 return false;
254
brettw@chromium.orgabe477a2011-01-21 13:55:52 +0900255 base::AutoLock scoped_lock(path_data->lock);
phajdan.jr@chromium.org26faa102010-09-10 08:49:04 +0900256
257 // Clear the cache now. Some of its entries could have depended
258 // on the value we are overriding, and are now out of sync with reality.
259 path_data->cache.clear();
260
jcampan@chromium.orgcce10e72009-06-26 02:29:09 +0900261 path_data->cache[key] = file_path;
phajdan.jr@chromium.org26faa102010-09-10 08:49:04 +0900262 path_data->overrides[key] = file_path;
263
initial.commit3f4a7322008-07-27 06:49:38 +0900264 return true;
265}
266
initial.commit3f4a7322008-07-27 06:49:38 +0900267void PathService::RegisterProvider(ProviderFunc func, int key_start,
268 int key_end) {
erikkay@google.coma3ff2752008-08-13 02:33:52 +0900269 PathData* path_data = GetPathData();
initial.commit3f4a7322008-07-27 06:49:38 +0900270 DCHECK(path_data);
kushi.p@gmail.come18fdd32011-03-14 07:13:33 +0900271 DCHECK_GT(key_end, key_start);
initial.commit3f4a7322008-07-27 06:49:38 +0900272
brettw@chromium.orgabe477a2011-01-21 13:55:52 +0900273 base::AutoLock scoped_lock(path_data->lock);
initial.commit3f4a7322008-07-27 06:49:38 +0900274
275 Provider* p;
276
277#ifndef NDEBUG
278 p = path_data->providers;
279 while (p) {
280 DCHECK(key_start >= p->key_end || key_end <= p->key_start) <<
281 "path provider collision";
282 p = p->next;
283 }
284#endif
285
286 p = new Provider;
erikkay@google.com564ef6d2008-08-21 00:47:39 +0900287 p->is_static = false;
initial.commit3f4a7322008-07-27 06:49:38 +0900288 p->func = func;
289 p->next = path_data->providers;
290#ifndef NDEBUG
291 p->key_start = key_start;
292 p->key_end = key_end;
293#endif
294 path_data->providers = p;
295}