1 // Copyright (c) 2012 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.
5 #include "base/path_service.h"
13 #include "base/file_path.h"
14 #include "base/file_util.h"
15 #include "base/hash_tables.h"
16 #include "base/lazy_instance.h"
17 #include "base/logging.h"
18 #include "base/synchronization/lock.h"
21 bool PathProvider(int key
, FilePath
* result
);
23 bool PathProviderWin(int key
, FilePath
* result
);
24 #elif defined(OS_MACOSX)
25 bool PathProviderMac(int key
, FilePath
* result
);
26 #elif defined(OS_ANDROID)
27 bool PathProviderAndroid(int key
, FilePath
* result
);
28 #elif defined(OS_POSIX)
29 // PathProviderPosix is the default path provider on POSIX OSes other than
31 bool PathProviderPosix(int key
, FilePath
* result
);
37 typedef base::hash_map
<int, FilePath
> PathMap
;
39 // We keep a linked list of providers. In a debug build we ensure that no two
40 // providers claim overlapping keys.
42 PathService::ProviderFunc func
;
43 struct Provider
* next
;
51 Provider base_provider
= {
62 Provider base_provider_win
= {
63 base::PathProviderWin
,
73 #if defined(OS_MACOSX)
74 Provider base_provider_mac
= {
75 base::PathProviderMac
,
85 #if defined(OS_ANDROID)
86 Provider base_provider_android
= {
87 base::PathProviderAndroid
,
90 base::PATH_ANDROID_START
,
91 base::PATH_ANDROID_END
,
97 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
98 Provider base_provider_posix
= {
99 base::PathProviderPosix
,
102 base::PATH_POSIX_START
,
103 base::PATH_POSIX_END
,
112 PathMap cache
; // Cache mappings from path key to path value.
113 PathMap overrides
; // Track path overrides.
114 Provider
* providers
; // Linked list of path service providers.
118 providers
= &base_provider_win
;
119 #elif defined(OS_MACOSX)
120 providers
= &base_provider_mac
;
121 #elif defined(OS_ANDROID)
122 providers
= &base_provider_android
;
123 #elif defined(OS_POSIX)
124 providers
= &base_provider_posix
;
129 Provider
* p
= providers
;
131 Provider
* next
= p
->next
;
139 static base::LazyInstance
<PathData
> g_path_data
= LAZY_INSTANCE_INITIALIZER
;
141 static PathData
* GetPathData() {
142 return g_path_data
.Pointer();
145 // Tries to find |key| in the cache. |path_data| should be locked by the caller!
146 bool LockedGetFromCache(int key
, const PathData
* path_data
, FilePath
* result
) {
147 // 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
;
156 // Tries to find |key| in the overrides map. |path_data| should be locked by the
158 bool LockedGetFromOverrides(int key
, PathData
* path_data
, FilePath
* result
) {
159 // check for an overridden version.
160 PathMap::const_iterator it
= path_data
->overrides
.find(key
);
161 if (it
!= path_data
->overrides
.end()) {
162 path_data
->cache
[key
] = it
->second
;
163 *result
= it
->second
;
171 // 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.
175 bool PathService::Get(int key
, FilePath
* result
) {
176 PathData
* path_data
= GetPathData();
179 DCHECK_GE(key
, base::DIR_CURRENT
);
181 // special case the current directory because it can never be cached
182 if (key
== base::DIR_CURRENT
)
183 return file_util::GetCurrentDirectory(result
);
185 Provider
* provider
= NULL
;
187 base::AutoLock
scoped_lock(path_data
->lock
);
188 if (LockedGetFromCache(key
, path_data
, result
))
191 if (LockedGetFromOverrides(key
, path_data
, result
))
194 // Get the beginning of the list while it is still locked.
195 provider
= path_data
->providers
;
200 // Iterating does not need the lock because only the list head might be
201 // modified on another thread.
203 if (provider
->func(key
, &path
))
205 DCHECK(path
.empty()) << "provider should not have modified path";
206 provider
= provider
->next
;
214 base::AutoLock
scoped_lock(path_data
->lock
);
215 path_data
->cache
[key
] = path
;
221 bool PathService::Override(int key
, const FilePath
& path
) {
222 // Just call the full function with true for the value of |create|.
223 return OverrideAndCreateIfNeeded(key
, path
, true);
227 bool PathService::OverrideAndCreateIfNeeded(int key
,
228 const FilePath
& path
,
230 PathData
* path_data
= GetPathData();
232 DCHECK_GT(key
, base::DIR_CURRENT
) << "invalid path key";
234 FilePath file_path
= path
;
236 // For some locations this will fail if called from inside the sandbox there-
237 // fore we protect this call with a flag.
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
))
247 // We need to have an absolute path, as extensions and plugins don't like
248 // relative paths, and will gladly crash the browser in CHECK()s if they get a
250 if (!file_util::AbsolutePath(&file_path
))
253 base::AutoLock
scoped_lock(path_data
->lock
);
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();
259 path_data
->overrides
[key
] = file_path
;
265 bool PathService::RemoveOverride(int key
) {
266 PathData
* path_data
= GetPathData();
269 base::AutoLock
scoped_lock(path_data
->lock
);
271 if (path_data
->overrides
.find(key
) == path_data
->overrides
.end())
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();
278 path_data
->overrides
.erase(key
);
284 void PathService::RegisterProvider(ProviderFunc func
, int key_start
,
286 PathData
* path_data
= GetPathData();
288 DCHECK_GT(key_end
, key_start
);
293 p
->is_static
= false;
296 p
->key_start
= key_start
;
297 p
->key_end
= key_end
;
300 base::AutoLock
scoped_lock(path_data
->lock
);
303 Provider
*iter
= path_data
->providers
;
305 DCHECK(key_start
>= iter
->key_end
|| key_end
<= iter
->key_start
) <<
306 "path provider collision";
311 p
->next
= path_data
->providers
;
312 path_data
->providers
= p
;