Add support for indeterminate checkbox on Windows classic theme.
[chromium-blink-merge.git] / base / path_service.cc
blob1fd2f6f900402dedc98d07533cf891f0e574fd40
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 #if defined(OS_WIN)
8 #include <windows.h>
9 #include <shellapi.h>
10 #include <shlobj.h>
11 #endif
13 #include "base/file_util.h"
14 #include "base/files/file_path.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 using base::FilePath;
22 namespace base {
23 bool PathProvider(int key, FilePath* result);
24 #if defined(OS_WIN)
25 bool PathProviderWin(int key, FilePath* result);
26 #elif defined(OS_MACOSX)
27 bool PathProviderMac(int key, FilePath* result);
28 #elif defined(OS_ANDROID)
29 bool PathProviderAndroid(int key, FilePath* result);
30 #elif defined(OS_POSIX)
31 // PathProviderPosix is the default path provider on POSIX OSes other than
32 // Mac and Android.
33 bool PathProviderPosix(int key, FilePath* result);
34 #endif
37 namespace {
39 typedef base::hash_map<int, FilePath> PathMap;
41 // We keep a linked list of providers. In a debug build we ensure that no two
42 // providers claim overlapping keys.
43 struct Provider {
44 PathService::ProviderFunc func;
45 struct Provider* next;
46 #ifndef NDEBUG
47 int key_start;
48 int key_end;
49 #endif
50 bool is_static;
53 Provider base_provider = {
54 base::PathProvider,
55 NULL,
56 #ifndef NDEBUG
57 base::PATH_START,
58 base::PATH_END,
59 #endif
60 true
63 #if defined(OS_WIN)
64 Provider base_provider_win = {
65 base::PathProviderWin,
66 &base_provider,
67 #ifndef NDEBUG
68 base::PATH_WIN_START,
69 base::PATH_WIN_END,
70 #endif
71 true
73 #endif
75 #if defined(OS_MACOSX)
76 Provider base_provider_mac = {
77 base::PathProviderMac,
78 &base_provider,
79 #ifndef NDEBUG
80 base::PATH_MAC_START,
81 base::PATH_MAC_END,
82 #endif
83 true
85 #endif
87 #if defined(OS_ANDROID)
88 Provider base_provider_android = {
89 base::PathProviderAndroid,
90 &base_provider,
91 #ifndef NDEBUG
92 base::PATH_ANDROID_START,
93 base::PATH_ANDROID_END,
94 #endif
95 true
97 #endif
99 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
100 Provider base_provider_posix = {
101 base::PathProviderPosix,
102 &base_provider,
103 #ifndef NDEBUG
104 base::PATH_POSIX_START,
105 base::PATH_POSIX_END,
106 #endif
107 true
109 #endif
112 struct PathData {
113 base::Lock lock;
114 PathMap cache; // Cache mappings from path key to path value.
115 PathMap overrides; // Track path overrides.
116 Provider* providers; // Linked list of path service providers.
117 bool cache_disabled; // Don't use cache if true;
119 PathData() : cache_disabled(false) {
120 #if defined(OS_WIN)
121 providers = &base_provider_win;
122 #elif defined(OS_MACOSX)
123 providers = &base_provider_mac;
124 #elif defined(OS_ANDROID)
125 providers = &base_provider_android;
126 #elif defined(OS_POSIX)
127 providers = &base_provider_posix;
128 #endif
131 ~PathData() {
132 Provider* p = providers;
133 while (p) {
134 Provider* next = p->next;
135 if (!p->is_static)
136 delete p;
137 p = next;
142 static base::LazyInstance<PathData> g_path_data = LAZY_INSTANCE_INITIALIZER;
144 static PathData* GetPathData() {
145 return g_path_data.Pointer();
148 // Tries to find |key| in the cache. |path_data| should be locked by the caller!
149 bool LockedGetFromCache(int key, const PathData* path_data, FilePath* result) {
150 if (path_data->cache_disabled)
151 return false;
152 // check for a cached version
153 PathMap::const_iterator it = path_data->cache.find(key);
154 if (it != path_data->cache.end()) {
155 *result = it->second;
156 return true;
158 return false;
161 // Tries to find |key| in the overrides map. |path_data| should be locked by the
162 // caller!
163 bool LockedGetFromOverrides(int key, PathData* path_data, FilePath* result) {
164 // check for an overridden version.
165 PathMap::const_iterator it = path_data->overrides.find(key);
166 if (it != path_data->overrides.end()) {
167 if (!path_data->cache_disabled)
168 path_data->cache[key] = it->second;
169 *result = it->second;
170 return true;
172 return false;
175 } // namespace
177 // TODO(brettw): this function does not handle long paths (filename > MAX_PATH)
178 // characters). This isn't supported very well by Windows right now, so it is
179 // moot, but we should keep this in mind for the future.
180 // static
181 bool PathService::Get(int key, FilePath* result) {
182 PathData* path_data = GetPathData();
183 DCHECK(path_data);
184 DCHECK(result);
185 DCHECK_GE(key, base::DIR_CURRENT);
187 // special case the current directory because it can never be cached
188 if (key == base::DIR_CURRENT)
189 return file_util::GetCurrentDirectory(result);
191 Provider* provider = NULL;
193 base::AutoLock scoped_lock(path_data->lock);
194 if (LockedGetFromCache(key, path_data, result))
195 return true;
197 if (LockedGetFromOverrides(key, path_data, result))
198 return true;
200 // Get the beginning of the list while it is still locked.
201 provider = path_data->providers;
204 FilePath path;
206 // Iterating does not need the lock because only the list head might be
207 // modified on another thread.
208 while (provider) {
209 if (provider->func(key, &path))
210 break;
211 DCHECK(path.empty()) << "provider should not have modified path";
212 provider = provider->next;
215 if (path.empty())
216 return false;
218 if (path.ReferencesParent()) {
219 // Make sure path service never returns a path with ".." in it.
220 if (!file_util::AbsolutePath(&path)) {
221 return false;
224 *result = path;
226 base::AutoLock scoped_lock(path_data->lock);
227 if (!path_data->cache_disabled)
228 path_data->cache[key] = path;
230 return true;
233 // static
234 bool PathService::Override(int key, const FilePath& path) {
235 // Just call the full function with true for the value of |create|.
236 return OverrideAndCreateIfNeeded(key, path, true);
239 // static
240 bool PathService::OverrideAndCreateIfNeeded(int key,
241 const FilePath& path,
242 bool create) {
243 PathData* path_data = GetPathData();
244 DCHECK(path_data);
245 DCHECK_GT(key, base::DIR_CURRENT) << "invalid path key";
247 FilePath file_path = path;
249 // For some locations this will fail if called from inside the sandbox there-
250 // fore we protect this call with a flag.
251 if (create) {
252 // Make sure the directory exists. We need to do this before we translate
253 // this to the absolute path because on POSIX, AbsolutePath fails if called
254 // on a non-existent path.
255 if (!file_util::PathExists(file_path) &&
256 !file_util::CreateDirectory(file_path))
257 return false;
260 // We need to have an absolute path, as extensions and plugins don't like
261 // relative paths, and will gladly crash the browser in CHECK()s if they get a
262 // relative path.
263 if (!file_util::AbsolutePath(&file_path))
264 return false;
266 base::AutoLock scoped_lock(path_data->lock);
268 // Clear the cache now. Some of its entries could have depended
269 // on the value we are overriding, and are now out of sync with reality.
270 path_data->cache.clear();
272 path_data->overrides[key] = file_path;
274 return true;
277 // static
278 bool PathService::RemoveOverride(int key) {
279 PathData* path_data = GetPathData();
280 DCHECK(path_data);
282 base::AutoLock scoped_lock(path_data->lock);
284 if (path_data->overrides.find(key) == path_data->overrides.end())
285 return false;
287 // Clear the cache now. Some of its entries could have depended on the value
288 // we are going to remove, and are now out of sync.
289 path_data->cache.clear();
291 path_data->overrides.erase(key);
293 return true;
296 // static
297 void PathService::RegisterProvider(ProviderFunc func, int key_start,
298 int key_end) {
299 PathData* path_data = GetPathData();
300 DCHECK(path_data);
301 DCHECK_GT(key_end, key_start);
303 Provider* p;
305 p = new Provider;
306 p->is_static = false;
307 p->func = func;
308 #ifndef NDEBUG
309 p->key_start = key_start;
310 p->key_end = key_end;
311 #endif
313 base::AutoLock scoped_lock(path_data->lock);
315 #ifndef NDEBUG
316 Provider *iter = path_data->providers;
317 while (iter) {
318 DCHECK(key_start >= iter->key_end || key_end <= iter->key_start) <<
319 "path provider collision";
320 iter = iter->next;
322 #endif
324 p->next = path_data->providers;
325 path_data->providers = p;
328 // static
329 void PathService::DisableCache() {
330 PathData* path_data = GetPathData();
331 DCHECK(path_data);
333 base::AutoLock scoped_lock(path_data->lock);
334 path_data->cache.clear();
335 path_data->cache_disabled = true;