path_service.cc revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
1// 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.
4
5#include "base/path_service.h"
6
7#ifdef OS_WIN
8#include <windows.h>
9#include <shellapi.h>
10#include <shlobj.h>
11#endif
12
13#include "base/file_path.h"
14#include "base/file_util.h"
15#include "base/hash_tables.h"
16#include "base/lock.h"
17#include "base/logging.h"
18#include "base/singleton.h"
19
20namespace 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_POSIX)
27  bool PathProviderPosix(int key, FilePath* result);
28#endif
29}
30
31namespace {
32
33typedef base::hash_map<int, FilePath> PathMap;
34
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
44  bool is_static;
45};
46
47static Provider base_provider = {
48  base::PathProvider,
49  NULL,
50#ifndef NDEBUG
51  base::PATH_START,
52  base::PATH_END,
53#endif
54  true
55};
56
57#if defined(OS_WIN)
58static Provider base_provider_win = {
59  base::PathProviderWin,
60  &base_provider,
61#ifndef NDEBUG
62  base::PATH_WIN_START,
63  base::PATH_WIN_END,
64#endif
65  true
66};
67#endif
68
69#if defined(OS_MACOSX)
70static Provider base_provider_mac = {
71  base::PathProviderMac,
72  &base_provider,
73#ifndef NDEBUG
74  base::PATH_MAC_START,
75  base::PATH_MAC_END,
76#endif
77  true
78};
79#endif
80
81#if defined(OS_POSIX) && !defined(OS_MACOSX)
82static Provider base_provider_posix = {
83  base::PathProviderPosix,
84  &base_provider,
85#ifndef NDEBUG
86  0,
87  0,
88#endif
89  true
90};
91#endif
92
93
94struct PathData {
95  Lock      lock;
96  PathMap   cache;      // Cache mappings from path key to path value.
97  PathMap   overrides;  // Track path overrides.
98  Provider* providers;  // Linked list of path service providers.
99
100  PathData() {
101#if defined(OS_WIN)
102    providers = &base_provider_win;
103#elif defined(OS_MACOSX)
104    providers = &base_provider_mac;
105#elif defined(OS_POSIX)
106    providers = &base_provider_posix;
107#endif
108  }
109
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  }
119};
120
121static PathData* GetPathData() {
122  return Singleton<PathData>::get();
123}
124
125}  // namespace
126
127
128// static
129bool PathService::GetFromCache(int key, FilePath* result) {
130  PathData* path_data = GetPathData();
131  AutoLock scoped_lock(path_data->lock);
132
133  // 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
142// static
143bool 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
157void PathService::AddToCache(int key, const FilePath& path) {
158  PathData* path_data = GetPathData();
159  AutoLock scoped_lock(path_data->lock);
160  // Save the computed path in our cache.
161  path_data->cache[key] = path;
162}
163
164// 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
168bool PathService::Get(int key, FilePath* result) {
169  PathData* path_data = GetPathData();
170  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
175  if (key == base::DIR_CURRENT)
176    return file_util::GetCurrentDirectory(result);
177
178  if (GetFromCache(key, result))
179    return true;
180
181  if (GetFromOverrides(key, result))
182    return true;
183
184  FilePath path;
185
186  // search providers for the requested path
187  // NOTE: it should be safe to iterate here without the lock
188  // since RegisterProvider always prepends.
189  Provider* provider = path_data->providers;
190  while (provider) {
191    if (provider->func(key, &path))
192      break;
193    DCHECK(path.empty()) << "provider should not have modified path";
194    provider = provider->next;
195  }
196
197  if (path.empty())
198    return false;
199
200  AddToCache(key, path);
201
202  *result = path;
203  return true;
204}
205
206#if defined(OS_WIN)
207// 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();
214  return true;
215}
216#endif
217
218bool PathService::Override(int key, const FilePath& path) {
219  PathData* path_data = GetPathData();
220  DCHECK(path_data);
221  DCHECK(key > base::DIR_CURRENT) << "invalid path key";
222
223  FilePath file_path = path;
224
225  // 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.
228  if (!file_util::PathExists(file_path) &&
229      !file_util::CreateDirectory(file_path))
230    return false;
231
232  // 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
238  AutoLock scoped_lock(path_data->lock);
239
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
244  path_data->cache[key] = file_path;
245  path_data->overrides[key] = file_path;
246
247  return true;
248}
249
250void PathService::RegisterProvider(ProviderFunc func, int key_start,
251                                   int key_end) {
252  PathData* path_data = GetPathData();
253  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;
270  p->is_static = false;
271  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}
279