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