[content shell] implement testRunner.overridePreference
[chromium-blink-merge.git] / base / path_service.cc
blob2697653b39ef1699ad330814fdd54e9e3227b2f7
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"
7 #ifdef OS_WIN
8 #include <windows.h>
9 #include <shellapi.h>
10 #include <shlobj.h>
11 #endif
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"
20 namespace base {
21 bool PathProvider(int key, FilePath* result);
22 #if defined(OS_WIN)
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
30 // Mac and Android.
31 bool PathProviderPosix(int key, FilePath* result);
32 #endif
35 namespace {
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.
41 struct Provider {
42 PathService::ProviderFunc func;
43 struct Provider* next;
44 #ifndef NDEBUG
45 int key_start;
46 int key_end;
47 #endif
48 bool is_static;
51 Provider base_provider = {
52 base::PathProvider,
53 NULL,
54 #ifndef NDEBUG
55 base::PATH_START,
56 base::PATH_END,
57 #endif
58 true
61 #if defined(OS_WIN)
62 Provider base_provider_win = {
63 base::PathProviderWin,
64 &base_provider,
65 #ifndef NDEBUG
66 base::PATH_WIN_START,
67 base::PATH_WIN_END,
68 #endif
69 true
71 #endif
73 #if defined(OS_MACOSX)
74 Provider base_provider_mac = {
75 base::PathProviderMac,
76 &base_provider,
77 #ifndef NDEBUG
78 base::PATH_MAC_START,
79 base::PATH_MAC_END,
80 #endif
81 true
83 #endif
85 #if defined(OS_ANDROID)
86 Provider base_provider_android = {
87 base::PathProviderAndroid,
88 &base_provider,
89 #ifndef NDEBUG
90 base::PATH_ANDROID_START,
91 base::PATH_ANDROID_END,
92 #endif
93 true
95 #endif
97 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
98 Provider base_provider_posix = {
99 base::PathProviderPosix,
100 &base_provider,
101 #ifndef NDEBUG
102 base::PATH_POSIX_START,
103 base::PATH_POSIX_END,
104 #endif
105 true
107 #endif
110 struct PathData {
111 base::Lock lock;
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.
116 PathData() {
117 #if defined(OS_WIN)
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;
125 #endif
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;
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;
151 return true;
153 return false;
156 // Tries to find |key| in the overrides map. |path_data| should be locked by the
157 // caller!
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;
164 return true;
166 return false;
169 } // namespace
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.
174 // static
175 bool PathService::Get(int key, FilePath* result) {
176 PathData* path_data = GetPathData();
177 DCHECK(path_data);
178 DCHECK(result);
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))
189 return true;
191 if (LockedGetFromOverrides(key, path_data, result))
192 return true;
194 // Get the beginning of the list while it is still locked.
195 provider = path_data->providers;
198 FilePath path;
200 // Iterating does not need the lock because only the list head might be
201 // modified on another thread.
202 while (provider) {
203 if (provider->func(key, &path))
204 break;
205 DCHECK(path.empty()) << "provider should not have modified path";
206 provider = provider->next;
209 if (path.empty())
210 return false;
212 *result = path;
214 base::AutoLock scoped_lock(path_data->lock);
215 path_data->cache[key] = path;
217 return true;
220 // static
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);
226 // static
227 bool PathService::OverrideAndCreateIfNeeded(int key,
228 const FilePath& path,
229 bool create) {
230 PathData* path_data = GetPathData();
231 DCHECK(path_data);
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.
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;
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
249 // relative path.
250 if (!file_util::AbsolutePath(&file_path))
251 return false;
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;
261 return true;
264 // static
265 bool PathService::RemoveOverride(int key) {
266 PathData* path_data = GetPathData();
267 DCHECK(path_data);
269 base::AutoLock scoped_lock(path_data->lock);
271 if (path_data->overrides.find(key) == path_data->overrides.end())
272 return false;
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);
280 return true;
283 // static
284 void PathService::RegisterProvider(ProviderFunc func, int key_start,
285 int key_end) {
286 PathData* path_data = GetPathData();
287 DCHECK(path_data);
288 DCHECK_GT(key_end, key_start);
290 Provider* p;
292 p = new Provider;
293 p->is_static = false;
294 p->func = func;
295 #ifndef NDEBUG
296 p->key_start = key_start;
297 p->key_end = key_end;
298 #endif
300 base::AutoLock scoped_lock(path_data->lock);
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;
309 #endif
311 p->next = path_data->providers;
312 path_data->providers = p;