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