blob: 8660c424c77cbdb24a853925dce249a2ae27ed45 [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// Copyright (c) 2006-2008 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.
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"
initial.commitd7cae122008-07-26 21:49:3816#include "base/lock.h"
17#include "base/logging.h"
[email protected]1265917f2008-08-12 17:33:5218#include "base/singleton.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]5d1937bb2009-11-21 01:29:0026#elif defined(OS_POSIX)
27 bool PathProviderPosix(int key, FilePath* result);
[email protected]ac510e12008-08-05 19:46:3128#endif
initial.commitd7cae122008-07-26 21:49:3829}
30
31namespace {
32
[email protected]640517f2008-10-30 23:54:0433typedef base::hash_map<int, FilePath> PathMap;
initial.commitd7cae122008-07-26 21:49:3834
35// We keep a linked list of providers. In a debug build we ensure that no two
36// providers claim overlapping keys.
37struct Provider {
38 PathService::ProviderFunc func;
39 struct Provider* next;
40#ifndef NDEBUG
41 int key_start;
42 int key_end;
43#endif
[email protected]173cb8a02008-08-20 15:47:3944 bool is_static;
initial.commitd7cae122008-07-26 21:49:3845};
46
47static Provider base_provider = {
48 base::PathProvider,
49 NULL,
50#ifndef NDEBUG
51 base::PATH_START,
[email protected]173cb8a02008-08-20 15:47:3952 base::PATH_END,
initial.commitd7cae122008-07-26 21:49:3853#endif
[email protected]173cb8a02008-08-20 15:47:3954 true
initial.commitd7cae122008-07-26 21:49:3855};
56
[email protected]5d1937bb2009-11-21 01:29:0057#if defined(OS_WIN)
[email protected]ac510e12008-08-05 19:46:3158static Provider base_provider_win = {
59 base::PathProviderWin,
60 &base_provider,
61#ifndef NDEBUG
62 base::PATH_WIN_START,
[email protected]173cb8a02008-08-20 15:47:3963 base::PATH_WIN_END,
[email protected]ac510e12008-08-05 19:46:3164#endif
[email protected]173cb8a02008-08-20 15:47:3965 true
[email protected]ac510e12008-08-05 19:46:3166};
67#endif
68
[email protected]5d1937bb2009-11-21 01:29:0069#if defined(OS_MACOSX)
[email protected]173cb8a02008-08-20 15:47:3970static Provider base_provider_mac = {
[email protected]5af2edb92008-08-08 20:16:0871 base::PathProviderMac,
72 &base_provider,
73#ifndef NDEBUG
74 base::PATH_MAC_START,
[email protected]173cb8a02008-08-20 15:47:3975 base::PATH_MAC_END,
[email protected]5af2edb92008-08-08 20:16:0876#endif
[email protected]173cb8a02008-08-20 15:47:3977 true
78};
[email protected]5af2edb92008-08-08 20:16:0879#endif
[email protected]4c0040c2008-08-15 01:04:1180
[email protected]5d1937bb2009-11-21 01:29:0081#if defined(OS_POSIX) && !defined(OS_MACOSX)
82static Provider base_provider_posix = {
83 base::PathProviderPosix,
[email protected]4c0040c2008-08-15 01:04:1184 &base_provider,
85#ifndef NDEBUG
[email protected]5d1937bb2009-11-21 01:29:0086 0,
87 0,
[email protected]4c0040c2008-08-15 01:04:1188#endif
[email protected]173cb8a02008-08-20 15:47:3989 true
90};
[email protected]4c0040c2008-08-15 01:04:1191#endif
92
93
initial.commitd7cae122008-07-26 21:49:3894struct PathData {
95 Lock lock;
[email protected]34e043b2010-09-09 23:49:0496 PathMap cache; // Cache mappings from path key to path value.
97 PathMap overrides; // Track path overrides.
initial.commitd7cae122008-07-26 21:49:3898 Provider* providers; // Linked list of path service providers.
99
[email protected]ac510e12008-08-05 19:46:31100 PathData() {
101#if defined(OS_WIN)
102 providers = &base_provider_win;
[email protected]5af2edb92008-08-08 20:16:08103#elif defined(OS_MACOSX)
104 providers = &base_provider_mac;
[email protected]5d1937bb2009-11-21 01:29:00105#elif defined(OS_POSIX)
106 providers = &base_provider_posix;
[email protected]ac510e12008-08-05 19:46:31107#endif
initial.commitd7cae122008-07-26 21:49:38108 }
[email protected]173cb8a02008-08-20 15:47:39109
110 ~PathData() {
111 Provider* p = providers;
112 while (p) {
113 Provider* next = p->next;
114 if (!p->is_static)
115 delete p;
116 p = next;
117 }
118 }
initial.commitd7cae122008-07-26 21:49:38119};
[email protected]52a261f2009-03-03 15:01:12120
[email protected]1265917f2008-08-12 17:33:52121static PathData* GetPathData() {
122 return Singleton<PathData>::get();
123}
initial.commitd7cae122008-07-26 21:49:38124
125} // namespace
126
[email protected]6723f832008-08-11 15:38:27127
[email protected]1265917f2008-08-12 17:33:52128// static
[email protected]640517f2008-10-30 23:54:04129bool PathService::GetFromCache(int key, FilePath* result) {
[email protected]1265917f2008-08-12 17:33:52130 PathData* path_data = GetPathData();
[email protected]6723f832008-08-11 15:38:27131 AutoLock scoped_lock(path_data->lock);
[email protected]52a261f2009-03-03 15:01:12132
[email protected]6723f832008-08-11 15:38:27133 // check for a cached version
134 PathMap::const_iterator it = path_data->cache.find(key);
135 if (it != path_data->cache.end()) {
136 *result = it->second;
137 return true;
138 }
139 return false;
140}
141
[email protected]1265917f2008-08-12 17:33:52142// static
[email protected]34e043b2010-09-09 23:49:04143bool PathService::GetFromOverrides(int key, FilePath* result) {
144 PathData* path_data = GetPathData();
145 AutoLock scoped_lock(path_data->lock);
146
147 // check for an overriden version.
148 PathMap::const_iterator it = path_data->overrides.find(key);
149 if (it != path_data->overrides.end()) {
150 *result = it->second;
151 return true;
152 }
153 return false;
154}
155
156// static
[email protected]640517f2008-10-30 23:54:04157void PathService::AddToCache(int key, const FilePath& path) {
[email protected]1265917f2008-08-12 17:33:52158 PathData* path_data = GetPathData();
[email protected]6723f832008-08-11 15:38:27159 AutoLock scoped_lock(path_data->lock);
160 // Save the computed path in our cache.
161 path_data->cache[key] = path;
162}
163
initial.commitd7cae122008-07-26 21:49:38164// TODO(brettw): this function does not handle long paths (filename > MAX_PATH)
165// characters). This isn't supported very well by Windows right now, so it is
166// moot, but we should keep this in mind for the future.
167// static
[email protected]640517f2008-10-30 23:54:04168bool PathService::Get(int key, FilePath* result) {
[email protected]1265917f2008-08-12 17:33:52169 PathData* path_data = GetPathData();
initial.commitd7cae122008-07-26 21:49:38170 DCHECK(path_data);
171 DCHECK(result);
172 DCHECK(key >= base::DIR_CURRENT);
173
174 // special case the current directory because it can never be cached
[email protected]5af2edb92008-08-08 20:16:08175 if (key == base::DIR_CURRENT)
[email protected]1010f7d2008-08-06 16:29:44176 return file_util::GetCurrentDirectory(result);
initial.commitd7cae122008-07-26 21:49:38177
[email protected]aa443bbd2010-08-19 23:20:09178 if (GetFromCache(key, result))
initial.commitd7cae122008-07-26 21:49:38179 return true;
[email protected]c1a9f8d42009-02-28 01:49:55180
[email protected]34e043b2010-09-09 23:49:04181 if (GetFromOverrides(key, result))
182 return true;
183
[email protected]4792a262008-11-19 16:50:03184 FilePath path;
initial.commitd7cae122008-07-26 21:49:38185
186 // search providers for the requested path
[email protected]6723f832008-08-11 15:38:27187 // NOTE: it should be safe to iterate here without the lock
188 // since RegisterProvider always prepends.
initial.commitd7cae122008-07-26 21:49:38189 Provider* provider = path_data->providers;
190 while (provider) {
[email protected]4792a262008-11-19 16:50:03191 if (provider->func(key, &path))
initial.commitd7cae122008-07-26 21:49:38192 break;
[email protected]c1a9f8d42009-02-28 01:49:55193 DCHECK(path.empty()) << "provider should not have modified path";
initial.commitd7cae122008-07-26 21:49:38194 provider = provider->next;
195 }
196
[email protected]c1a9f8d42009-02-28 01:49:55197 if (path.empty())
initial.commitd7cae122008-07-26 21:49:38198 return false;
199
[email protected]6723f832008-08-11 15:38:27200 AddToCache(key, path);
[email protected]c1a9f8d42009-02-28 01:49:55201
[email protected]640517f2008-10-30 23:54:04202 *result = path;
[email protected]640517f2008-10-30 23:54:04203 return true;
204}
205
[email protected]ab926d552009-10-19 22:54:41206#if defined(OS_WIN)
[email protected]640517f2008-10-30 23:54:04207// static
208bool PathService::Get(int key, std::wstring* result) {
209 // Deprecated compatibility function.
210 FilePath path;
211 if (!Get(key, &path))
212 return false;
213 *result = path.ToWStringHack();
initial.commitd7cae122008-07-26 21:49:38214 return true;
215}
[email protected]ab926d552009-10-19 22:54:41216#endif
initial.commitd7cae122008-07-26 21:49:38217
[email protected]eca6a4f2009-06-25 17:29:09218bool PathService::Override(int key, const FilePath& path) {
[email protected]1265917f2008-08-12 17:33:52219 PathData* path_data = GetPathData();
initial.commitd7cae122008-07-26 21:49:38220 DCHECK(path_data);
221 DCHECK(key > base::DIR_CURRENT) << "invalid path key";
222
[email protected]eca6a4f2009-06-25 17:29:09223 FilePath file_path = path;
initial.commitd7cae122008-07-26 21:49:38224
[email protected]dabdb682009-10-27 23:31:36225 // Make sure the directory exists. We need to do this before we translate
226 // this to the absolute path because on POSIX, AbsolutePath fails if called
227 // on a non-existant path.
[email protected]eca6a4f2009-06-25 17:29:09228 if (!file_util::PathExists(file_path) &&
229 !file_util::CreateDirectory(file_path))
initial.commitd7cae122008-07-26 21:49:38230 return false;
231
[email protected]dabdb682009-10-27 23:31:36232 // We need to have an absolute path, as extensions and plugins don't like
233 // relative paths, and will glady crash the browser in CHECK()s if they get a
234 // relative path.
235 if (!file_util::AbsolutePath(&file_path))
236 return false;
237
initial.commitd7cae122008-07-26 21:49:38238 AutoLock scoped_lock(path_data->lock);
[email protected]34e043b2010-09-09 23:49:04239
240 // Clear the cache now. Some of its entries could have depended
241 // on the value we are overriding, and are now out of sync with reality.
242 path_data->cache.clear();
243
[email protected]eca6a4f2009-06-25 17:29:09244 path_data->cache[key] = file_path;
[email protected]34e043b2010-09-09 23:49:04245 path_data->overrides[key] = file_path;
246
initial.commitd7cae122008-07-26 21:49:38247 return true;
248}
249
initial.commitd7cae122008-07-26 21:49:38250void PathService::RegisterProvider(ProviderFunc func, int key_start,
251 int key_end) {
[email protected]1265917f2008-08-12 17:33:52252 PathData* path_data = GetPathData();
initial.commitd7cae122008-07-26 21:49:38253 DCHECK(path_data);
254 DCHECK(key_end > key_start);
255
256 AutoLock scoped_lock(path_data->lock);
257
258 Provider* p;
259
260#ifndef NDEBUG
261 p = path_data->providers;
262 while (p) {
263 DCHECK(key_start >= p->key_end || key_end <= p->key_start) <<
264 "path provider collision";
265 p = p->next;
266 }
267#endif
268
269 p = new Provider;
[email protected]173cb8a02008-08-20 15:47:39270 p->is_static = false;
initial.commitd7cae122008-07-26 21:49:38271 p->func = func;
272 p->next = path_data->providers;
273#ifndef NDEBUG
274 p->key_start = key_start;
275 p->key_end = key_end;
276#endif
277 path_data->providers = p;
278}