path_service.cc revision c7f5f8508d98d5952d42ed7648c2a8f30a4da156
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#include "base/string_util.h"
20
21namespace base {
22  bool PathProvider(int key, FilePath* result);
23#if defined(OS_WIN)
24  bool PathProviderWin(int key, FilePath* result);
25#elif defined(OS_MACOSX)
26  bool PathProviderMac(int key, FilePath* result);
27#elif defined(OS_POSIX)
28  bool PathProviderPosix(int key, FilePath* result);
29#endif
30}
31
32namespace {
33
34typedef base::hash_map<int, FilePath> PathMap;
35typedef base::hash_set<int> PathSet;
36
37// We keep a linked list of providers.  In a debug build we ensure that no two
38// providers claim overlapping keys.
39struct Provider {
40  PathService::ProviderFunc func;
41  struct Provider* next;
42#ifndef NDEBUG
43  int key_start;
44  int key_end;
45#endif
46  bool is_static;
47};
48
49static Provider base_provider = {
50  base::PathProvider,
51  NULL,
52#ifndef NDEBUG
53  base::PATH_START,
54  base::PATH_END,
55#endif
56  true
57};
58
59#if defined(OS_WIN)
60static Provider base_provider_win = {
61  base::PathProviderWin,
62  &base_provider,
63#ifndef NDEBUG
64  base::PATH_WIN_START,
65  base::PATH_WIN_END,
66#endif
67  true
68};
69#endif
70
71#if defined(OS_MACOSX)
72static Provider base_provider_mac = {
73  base::PathProviderMac,
74  &base_provider,
75#ifndef NDEBUG
76  base::PATH_MAC_START,
77  base::PATH_MAC_END,
78#endif
79  true
80};
81#endif
82
83#if defined(OS_POSIX) && !defined(OS_MACOSX)
84static Provider base_provider_posix = {
85  base::PathProviderPosix,
86  &base_provider,
87#ifndef NDEBUG
88  0,
89  0,
90#endif
91  true
92};
93#endif
94
95
96struct PathData {
97  Lock      lock;
98  PathMap   cache;      // Track mappings from path key to path value.
99  PathSet   overrides;  // Track whether a path has been overridden.
100  Provider* providers;  // Linked list of path service providers.
101
102  PathData() {
103#if defined(OS_WIN)
104    providers = &base_provider_win;
105#elif defined(OS_MACOSX)
106    providers = &base_provider_mac;
107#elif defined(OS_POSIX)
108    providers = &base_provider_posix;
109#endif
110  }
111
112  ~PathData() {
113    Provider* p = providers;
114    while (p) {
115      Provider* next = p->next;
116      if (!p->is_static)
117        delete p;
118      p = next;
119    }
120  }
121};
122
123static PathData* GetPathData() {
124  return Singleton<PathData>::get();
125}
126
127}  // namespace
128
129
130// static
131bool PathService::GetFromCache(int key, FilePath* result) {
132  PathData* path_data = GetPathData();
133  AutoLock scoped_lock(path_data->lock);
134
135  // check for a cached version
136  PathMap::const_iterator it = path_data->cache.find(key);
137  if (it != path_data->cache.end()) {
138    *result = it->second;
139    return true;
140  }
141  return false;
142}
143
144// static
145void PathService::AddToCache(int key, const FilePath& path) {
146  PathData* path_data = GetPathData();
147  AutoLock scoped_lock(path_data->lock);
148  // Save the computed path in our cache.
149  path_data->cache[key] = path;
150}
151
152// TODO(brettw): this function does not handle long paths (filename > MAX_PATH)
153// characters). This isn't supported very well by Windows right now, so it is
154// moot, but we should keep this in mind for the future.
155// static
156bool PathService::Get(int key, FilePath* result) {
157  PathData* path_data = GetPathData();
158  DCHECK(path_data);
159  DCHECK(result);
160  DCHECK(key >= base::DIR_CURRENT);
161
162  // special case the current directory because it can never be cached
163  if (key == base::DIR_CURRENT)
164    return file_util::GetCurrentDirectory(result);
165
166  if (GetFromCache(key, result))
167    return true;
168
169  FilePath path;
170
171  // search providers for the requested path
172  // NOTE: it should be safe to iterate here without the lock
173  // since RegisterProvider always prepends.
174  Provider* provider = path_data->providers;
175  while (provider) {
176    if (provider->func(key, &path))
177      break;
178    DCHECK(path.empty()) << "provider should not have modified path";
179    provider = provider->next;
180  }
181
182  if (path.empty())
183    return false;
184
185  AddToCache(key, path);
186
187  *result = path;
188  return true;
189}
190
191#if defined(OS_WIN)
192// static
193bool PathService::Get(int key, std::wstring* result) {
194  // Deprecated compatibility function.
195  FilePath path;
196  if (!Get(key, &path))
197    return false;
198  *result = path.ToWStringHack();
199  return true;
200}
201#endif
202
203bool PathService::IsOverridden(int key) {
204  PathData* path_data = GetPathData();
205  DCHECK(path_data);
206
207  AutoLock scoped_lock(path_data->lock);
208  return path_data->overrides.find(key) != path_data->overrides.end();
209}
210
211bool PathService::Override(int key, const FilePath& path) {
212  PathData* path_data = GetPathData();
213  DCHECK(path_data);
214  DCHECK(key > base::DIR_CURRENT) << "invalid path key";
215
216  FilePath file_path = path;
217
218  // Make sure the directory exists. We need to do this before we translate
219  // this to the absolute path because on POSIX, AbsolutePath fails if called
220  // on a non-existant path.
221  if (!file_util::PathExists(file_path) &&
222      !file_util::CreateDirectory(file_path))
223    return false;
224
225  // We need to have an absolute path, as extensions and plugins don't like
226  // relative paths, and will glady crash the browser in CHECK()s if they get a
227  // relative path.
228  if (!file_util::AbsolutePath(&file_path))
229    return false;
230
231  AutoLock scoped_lock(path_data->lock);
232  path_data->cache[key] = file_path;
233  path_data->overrides.insert(key);
234  return true;
235}
236
237void PathService::RegisterProvider(ProviderFunc func, int key_start,
238                                   int key_end) {
239  PathData* path_data = GetPathData();
240  DCHECK(path_data);
241  DCHECK(key_end > key_start);
242
243  AutoLock scoped_lock(path_data->lock);
244
245  Provider* p;
246
247#ifndef NDEBUG
248  p = path_data->providers;
249  while (p) {
250    DCHECK(key_start >= p->key_end || key_end <= p->key_start) <<
251      "path provider collision";
252    p = p->next;
253  }
254#endif
255
256  p = new Provider;
257  p->is_static = false;
258  p->func = func;
259  p->next = path_data->providers;
260#ifndef NDEBUG
261  p->key_start = key_start;
262  p->key_end = key_end;
263#endif
264  path_data->providers = p;
265}
266