blob: 2697653b39ef1699ad330814fdd54e9e3227b2f7 [file] [log] [blame]
[email protected]cb571e752012-05-09 10:50:101// Copyright (c) 2012 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commitd7cae122008-07-26 21:49:384
[email protected]09ad1e622008-08-07 20:23:095#include "base/path_service.h"
[email protected]355cc2742008-08-06 16:01:256
7#ifdef OS_WIN
initial.commitd7cae122008-07-26 21:49:388#include <windows.h>
9#include <shellapi.h>
10#include <shlobj.h>
[email protected]355cc2742008-08-06 16:01:2511#endif
initial.commitd7cae122008-07-26 21:49:3812
[email protected]640517f2008-10-30 23:54:0413#include "base/file_path.h"
[email protected]09ad1e622008-08-07 20:23:0914#include "base/file_util.h"
[email protected]640517f2008-10-30 23:54:0415#include "base/hash_tables.h"
[email protected]625332e02010-12-14 07:48:4916#include "base/lazy_instance.h"
initial.commitd7cae122008-07-26 21:49:3817#include "base/logging.h"
[email protected]20305ec2011-01-21 04:55:5218#include "base/synchronization/lock.h"
initial.commitd7cae122008-07-26 21:49:3819
20namespace base {
[email protected]4792a262008-11-19 16:50:0321 bool PathProvider(int key, FilePath* result);
[email protected]5af2edb92008-08-08 20:16:0822#if defined(OS_WIN)
[email protected]4792a262008-11-19 16:50:0323 bool PathProviderWin(int key, FilePath* result);
[email protected]4c0040c2008-08-15 01:04:1124#elif defined(OS_MACOSX)
[email protected]4792a262008-11-19 16:50:0325 bool PathProviderMac(int key, FilePath* result);
[email protected]f7d69972011-06-21 22:34:5026#elif defined(OS_ANDROID)
27 bool PathProviderAndroid(int key, FilePath* result);
[email protected]5d1937bb2009-11-21 01:29:0028#elif defined(OS_POSIX)
[email protected]dea1d7d2012-09-20 16:24:5229 // PathProviderPosix is the default path provider on POSIX OSes other than
30 // Mac and Android.
[email protected]5d1937bb2009-11-21 01:29:0031 bool PathProviderPosix(int key, FilePath* result);
[email protected]ac510e12008-08-05 19:46:3132#endif
initial.commitd7cae122008-07-26 21:49:3833}
34
35namespace {
36
[email protected]640517f2008-10-30 23:54:0437typedef base::hash_map<int, FilePath> PathMap;
initial.commitd7cae122008-07-26 21:49:3838
39// We keep a linked list of providers. In a debug build we ensure that no two
40// providers claim overlapping keys.
41struct Provider {
42 PathService::ProviderFunc func;
43 struct Provider* next;
44#ifndef NDEBUG
45 int key_start;
46 int key_end;
47#endif
[email protected]173cb8a02008-08-20 15:47:3948 bool is_static;
initial.commitd7cae122008-07-26 21:49:3849};
50
[email protected]da9ccfb92012-01-28 00:34:4051Provider base_provider = {
initial.commitd7cae122008-07-26 21:49:3852 base::PathProvider,
53 NULL,
54#ifndef NDEBUG
55 base::PATH_START,
[email protected]173cb8a02008-08-20 15:47:3956 base::PATH_END,
initial.commitd7cae122008-07-26 21:49:3857#endif
[email protected]173cb8a02008-08-20 15:47:3958 true
initial.commitd7cae122008-07-26 21:49:3859};
60
[email protected]5d1937bb2009-11-21 01:29:0061#if defined(OS_WIN)
[email protected]da9ccfb92012-01-28 00:34:4062Provider base_provider_win = {
[email protected]ac510e12008-08-05 19:46:3163 base::PathProviderWin,
64 &base_provider,
65#ifndef NDEBUG
66 base::PATH_WIN_START,
[email protected]173cb8a02008-08-20 15:47:3967 base::PATH_WIN_END,
[email protected]ac510e12008-08-05 19:46:3168#endif
[email protected]173cb8a02008-08-20 15:47:3969 true
[email protected]ac510e12008-08-05 19:46:3170};
71#endif
72
[email protected]5d1937bb2009-11-21 01:29:0073#if defined(OS_MACOSX)
[email protected]da9ccfb92012-01-28 00:34:4074Provider base_provider_mac = {
[email protected]5af2edb92008-08-08 20:16:0875 base::PathProviderMac,
76 &base_provider,
77#ifndef NDEBUG
78 base::PATH_MAC_START,
[email protected]173cb8a02008-08-20 15:47:3979 base::PATH_MAC_END,
[email protected]5af2edb92008-08-08 20:16:0880#endif
[email protected]173cb8a02008-08-20 15:47:3981 true
82};
[email protected]5af2edb92008-08-08 20:16:0883#endif
[email protected]4c0040c2008-08-15 01:04:1184
[email protected]f7d69972011-06-21 22:34:5085#if defined(OS_ANDROID)
[email protected]da9ccfb92012-01-28 00:34:4086Provider base_provider_android = {
[email protected]f7d69972011-06-21 22:34:5087 base::PathProviderAndroid,
88 &base_provider,
89#ifndef NDEBUG
[email protected]dea1d7d2012-09-20 16:24:5290 base::PATH_ANDROID_START,
91 base::PATH_ANDROID_END,
[email protected]f7d69972011-06-21 22:34:5092#endif
93 true
94};
95#endif
96
97#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
[email protected]da9ccfb92012-01-28 00:34:4098Provider base_provider_posix = {
[email protected]5d1937bb2009-11-21 01:29:0099 base::PathProviderPosix,
[email protected]4c0040c2008-08-15 01:04:11100 &base_provider,
101#ifndef NDEBUG
[email protected]dea1d7d2012-09-20 16:24:52102 base::PATH_POSIX_START,
103 base::PATH_POSIX_END,
[email protected]4c0040c2008-08-15 01:04:11104#endif
[email protected]173cb8a02008-08-20 15:47:39105 true
106};
[email protected]4c0040c2008-08-15 01:04:11107#endif
108
109
initial.commitd7cae122008-07-26 21:49:38110struct PathData {
[email protected]20305ec2011-01-21 04:55:52111 base::Lock lock;
112 PathMap cache; // Cache mappings from path key to path value.
113 PathMap overrides; // Track path overrides.
initial.commitd7cae122008-07-26 21:49:38114 Provider* providers; // Linked list of path service providers.
115
[email protected]ac510e12008-08-05 19:46:31116 PathData() {
117#if defined(OS_WIN)
118 providers = &base_provider_win;
[email protected]5af2edb92008-08-08 20:16:08119#elif defined(OS_MACOSX)
120 providers = &base_provider_mac;
[email protected]f7d69972011-06-21 22:34:50121#elif defined(OS_ANDROID)
122 providers = &base_provider_android;
[email protected]5d1937bb2009-11-21 01:29:00123#elif defined(OS_POSIX)
124 providers = &base_provider_posix;
[email protected]ac510e12008-08-05 19:46:31125#endif
initial.commitd7cae122008-07-26 21:49:38126 }
[email protected]173cb8a02008-08-20 15:47:39127
128 ~PathData() {
129 Provider* p = providers;
130 while (p) {
131 Provider* next = p->next;
132 if (!p->is_static)
133 delete p;
134 p = next;
135 }
136 }
initial.commitd7cae122008-07-26 21:49:38137};
[email protected]52a261f2009-03-03 15:01:12138
[email protected]6de0fd1d2011-11-15 13:31:49139static base::LazyInstance<PathData> g_path_data = LAZY_INSTANCE_INITIALIZER;
[email protected]625332e02010-12-14 07:48:49140
[email protected]1265917f2008-08-12 17:33:52141static PathData* GetPathData() {
[email protected]625332e02010-12-14 07:48:49142 return g_path_data.Pointer();
[email protected]1265917f2008-08-12 17:33:52143}
initial.commitd7cae122008-07-26 21:49:38144
[email protected]d6b3af92012-09-26 19:05:12145// Tries to find |key| in the cache. |path_data| should be locked by the caller!
146bool LockedGetFromCache(int key, const PathData* path_data, FilePath* result) {
[email protected]6723f832008-08-11 15:38:27147 // check for a cached version
148 PathMap::const_iterator it = path_data->cache.find(key);
149 if (it != path_data->cache.end()) {
150 *result = it->second;
151 return true;
152 }
153 return false;
154}
155
[email protected]d6b3af92012-09-26 19:05:12156// Tries to find |key| in the overrides map. |path_data| should be locked by the
157// caller!
158bool LockedGetFromOverrides(int key, PathData* path_data, FilePath* result) {
[email protected]846c3ecea2011-12-14 18:47:26159 // check for an overridden version.
[email protected]34e043b2010-09-09 23:49:04160 PathMap::const_iterator it = path_data->overrides.find(key);
161 if (it != path_data->overrides.end()) {
[email protected]d6b3af92012-09-26 19:05:12162 path_data->cache[key] = it->second;
[email protected]34e043b2010-09-09 23:49:04163 *result = it->second;
164 return true;
165 }
166 return false;
167}
168
[email protected]d6b3af92012-09-26 19:05:12169} // namespace
[email protected]6723f832008-08-11 15:38:27170
initial.commitd7cae122008-07-26 21:49:38171// TODO(brettw): this function does not handle long paths (filename > MAX_PATH)
172// characters). This isn't supported very well by Windows right now, so it is
173// moot, but we should keep this in mind for the future.
174// static
[email protected]640517f2008-10-30 23:54:04175bool PathService::Get(int key, FilePath* result) {
[email protected]1265917f2008-08-12 17:33:52176 PathData* path_data = GetPathData();
initial.commitd7cae122008-07-26 21:49:38177 DCHECK(path_data);
178 DCHECK(result);
[email protected]8e03fec2011-03-31 20:34:25179 DCHECK_GE(key, base::DIR_CURRENT);
initial.commitd7cae122008-07-26 21:49:38180
181 // special case the current directory because it can never be cached
[email protected]5af2edb92008-08-08 20:16:08182 if (key == base::DIR_CURRENT)
[email protected]1010f7d12008-08-06 16:29:44183 return file_util::GetCurrentDirectory(result);
initial.commitd7cae122008-07-26 21:49:38184
[email protected]d6b3af92012-09-26 19:05:12185 Provider* provider = NULL;
186 {
187 base::AutoLock scoped_lock(path_data->lock);
188 if (LockedGetFromCache(key, path_data, result))
189 return true;
[email protected]c1a9f8d42009-02-28 01:49:55190
[email protected]d6b3af92012-09-26 19:05:12191 if (LockedGetFromOverrides(key, path_data, result))
192 return true;
193
194 // Get the beginning of the list while it is still locked.
195 provider = path_data->providers;
196 }
[email protected]34e043b2010-09-09 23:49:04197
[email protected]4792a262008-11-19 16:50:03198 FilePath path;
initial.commitd7cae122008-07-26 21:49:38199
[email protected]d6b3af92012-09-26 19:05:12200 // Iterating does not need the lock because only the list head might be
201 // modified on another thread.
initial.commitd7cae122008-07-26 21:49:38202 while (provider) {
[email protected]4792a262008-11-19 16:50:03203 if (provider->func(key, &path))
initial.commitd7cae122008-07-26 21:49:38204 break;
[email protected]c1a9f8d42009-02-28 01:49:55205 DCHECK(path.empty()) << "provider should not have modified path";
initial.commitd7cae122008-07-26 21:49:38206 provider = provider->next;
207 }
208
[email protected]c1a9f8d42009-02-28 01:49:55209 if (path.empty())
initial.commitd7cae122008-07-26 21:49:38210 return false;
211
[email protected]640517f2008-10-30 23:54:04212 *result = path;
[email protected]d6b3af92012-09-26 19:05:12213
214 base::AutoLock scoped_lock(path_data->lock);
215 path_data->cache[key] = path;
216
[email protected]640517f2008-10-30 23:54:04217 return true;
218}
219
[email protected]d6b3af92012-09-26 19:05:12220// static
[email protected]eca6a4f2009-06-25 17:29:09221bool PathService::Override(int key, const FilePath& path) {
[email protected]cb571e752012-05-09 10:50:10222 // Just call the full function with true for the value of |create|.
223 return OverrideAndCreateIfNeeded(key, path, true);
224}
225
[email protected]d6b3af92012-09-26 19:05:12226// static
[email protected]cb571e752012-05-09 10:50:10227bool PathService::OverrideAndCreateIfNeeded(int key,
228 const FilePath& path,
229 bool create) {
[email protected]1265917f2008-08-12 17:33:52230 PathData* path_data = GetPathData();
initial.commitd7cae122008-07-26 21:49:38231 DCHECK(path_data);
[email protected]88563f62011-03-13 22:13:33232 DCHECK_GT(key, base::DIR_CURRENT) << "invalid path key";
initial.commitd7cae122008-07-26 21:49:38233
[email protected]eca6a4f2009-06-25 17:29:09234 FilePath file_path = path;
initial.commitd7cae122008-07-26 21:49:38235
[email protected]cb571e752012-05-09 10:50:10236 // For some locations this will fail if called from inside the sandbox there-
237 // fore we protect this call with a flag.
238 if (create) {
239 // Make sure the directory exists. We need to do this before we translate
240 // this to the absolute path because on POSIX, AbsolutePath fails if called
241 // on a non-existent path.
242 if (!file_util::PathExists(file_path) &&
243 !file_util::CreateDirectory(file_path))
244 return false;
245 }
initial.commitd7cae122008-07-26 21:49:38246
[email protected]dabdb682009-10-27 23:31:36247 // We need to have an absolute path, as extensions and plugins don't like
[email protected]846c3ecea2011-12-14 18:47:26248 // relative paths, and will gladly crash the browser in CHECK()s if they get a
[email protected]dabdb682009-10-27 23:31:36249 // relative path.
250 if (!file_util::AbsolutePath(&file_path))
251 return false;
252
[email protected]20305ec2011-01-21 04:55:52253 base::AutoLock scoped_lock(path_data->lock);
[email protected]34e043b2010-09-09 23:49:04254
255 // Clear the cache now. Some of its entries could have depended
256 // on the value we are overriding, and are now out of sync with reality.
257 path_data->cache.clear();
258
[email protected]34e043b2010-09-09 23:49:04259 path_data->overrides[key] = file_path;
260
initial.commitd7cae122008-07-26 21:49:38261 return true;
262}
263
[email protected]d6b3af92012-09-26 19:05:12264// static
265bool PathService::RemoveOverride(int key) {
266 PathData* path_data = GetPathData();
267 DCHECK(path_data);
268
269 base::AutoLock scoped_lock(path_data->lock);
270
271 if (path_data->overrides.find(key) == path_data->overrides.end())
272 return false;
273
274 // Clear the cache now. Some of its entries could have depended on the value
275 // we are going to remove, and are now out of sync.
276 path_data->cache.clear();
277
278 path_data->overrides.erase(key);
279
280 return true;
281}
282
283// static
initial.commitd7cae122008-07-26 21:49:38284void PathService::RegisterProvider(ProviderFunc func, int key_start,
285 int key_end) {
[email protected]1265917f2008-08-12 17:33:52286 PathData* path_data = GetPathData();
initial.commitd7cae122008-07-26 21:49:38287 DCHECK(path_data);
[email protected]88563f62011-03-13 22:13:33288 DCHECK_GT(key_end, key_start);
initial.commitd7cae122008-07-26 21:49:38289
initial.commitd7cae122008-07-26 21:49:38290 Provider* p;
291
initial.commitd7cae122008-07-26 21:49:38292 p = new Provider;
[email protected]173cb8a02008-08-20 15:47:39293 p->is_static = false;
initial.commitd7cae122008-07-26 21:49:38294 p->func = func;
initial.commitd7cae122008-07-26 21:49:38295#ifndef NDEBUG
296 p->key_start = key_start;
297 p->key_end = key_end;
298#endif
[email protected]d6b3af92012-09-26 19:05:12299
300 base::AutoLock scoped_lock(path_data->lock);
301
302#ifndef NDEBUG
303 Provider *iter = path_data->providers;
304 while (iter) {
305 DCHECK(key_start >= iter->key_end || key_end <= iter->key_start) <<
306 "path provider collision";
307 iter = iter->next;
308 }
309#endif
310
311 p->next = path_data->providers;
initial.commitd7cae122008-07-26 21:49:38312 path_data->providers = p;
313}