1 // Copyright (c) 2012 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 "webkit/browser/appcache/appcache_disk_cache.h" 6 7 #include "base/bind.h" 8 #include "base/bind_helpers.h" 9 #include "base/files/file_path.h" 10 #include "base/logging.h" 11 #include "base/stl_util.h" 12 #include "base/strings/string_number_conversions.h" 13 #include "net/base/cache_type.h" 14 #include "net/base/net_errors.h" 15 16 namespace appcache { 17 18 // A callback shim that provides storage for the 'backend_ptr' value 19 // and will delete a resulting ptr if completion occurs after its 20 // been canceled. 21 class AppCacheDiskCache::CreateBackendCallbackShim 22 : public base::RefCounted<CreateBackendCallbackShim> { 23 public: 24 explicit CreateBackendCallbackShim(AppCacheDiskCache* object) 25 : appcache_diskcache_(object) { 26 } 27 28 void Cancel() { 29 appcache_diskcache_ = NULL; 30 } 31 32 void Callback(int rv) { 33 if (appcache_diskcache_) 34 appcache_diskcache_->OnCreateBackendComplete(rv); 35 } 36 37 scoped_ptr<disk_cache::Backend> backend_ptr_; // Accessed directly. 38 39 private: 40 friend class base::RefCounted<CreateBackendCallbackShim>; 41 42 ~CreateBackendCallbackShim() { 43 } 44 45 AppCacheDiskCache* appcache_diskcache_; // Unowned pointer. 46 }; 47 48 // An implementation of AppCacheDiskCacheInterface::Entry that's a thin 49 // wrapper around disk_cache::Entry. 50 class AppCacheDiskCache::EntryImpl : public Entry { 51 public: 52 EntryImpl(disk_cache::Entry* disk_cache_entry, 53 AppCacheDiskCache* owner) 54 : disk_cache_entry_(disk_cache_entry), owner_(owner) { 55 DCHECK(disk_cache_entry); 56 DCHECK(owner); 57 owner_->AddOpenEntry(this); 58 } 59 60 // Entry implementation. 61 virtual int Read(int index, int64 offset, net::IOBuffer* buf, int buf_len, 62 const net::CompletionCallback& callback) OVERRIDE { 63 if (offset < 0 || offset > kint32max) 64 return net::ERR_INVALID_ARGUMENT; 65 if (!disk_cache_entry_) 66 return net::ERR_ABORTED; 67 return disk_cache_entry_->ReadData( 68 index, static_cast<int>(offset), buf, buf_len, callback); 69 } 70 71 virtual int Write(int index, int64 offset, net::IOBuffer* buf, int buf_len, 72 const net::CompletionCallback& callback) OVERRIDE { 73 if (offset < 0 || offset > kint32max) 74 return net::ERR_INVALID_ARGUMENT; 75 if (!disk_cache_entry_) 76 return net::ERR_ABORTED; 77 const bool kTruncate = true; 78 return disk_cache_entry_->WriteData( 79 index, static_cast<int>(offset), buf, buf_len, callback, kTruncate); 80 } 81 82 virtual int64 GetSize(int index) OVERRIDE { 83 return disk_cache_entry_ ? disk_cache_entry_->GetDataSize(index) : 0L; 84 } 85 86 virtual void Close() OVERRIDE { 87 if (disk_cache_entry_) 88 disk_cache_entry_->Close(); 89 delete this; 90 } 91 92 void Abandon() { 93 owner_ = NULL; 94 disk_cache_entry_->Close(); 95 disk_cache_entry_ = NULL; 96 } 97 98 private: 99 virtual ~EntryImpl() { 100 if (owner_) 101 owner_->RemoveOpenEntry(this); 102 } 103 104 disk_cache::Entry* disk_cache_entry_; 105 AppCacheDiskCache* owner_; 106 }; 107 108 // Separate object to hold state for each Create, Delete, or Doom call 109 // while the call is in-flight and to produce an EntryImpl upon completion. 110 class AppCacheDiskCache::ActiveCall { 111 public: 112 explicit ActiveCall(AppCacheDiskCache* owner) 113 : entry_(NULL), 114 owner_(owner), 115 entry_ptr_(NULL) { 116 } 117 118 int CreateEntry(int64 key, Entry** entry, 119 const net::CompletionCallback& callback) { 120 int rv = owner_->disk_cache()->CreateEntry( 121 base::Int64ToString(key), &entry_ptr_, 122 base::Bind(&ActiveCall::OnAsyncCompletion, base::Unretained(this))); 123 return HandleImmediateReturnValue(rv, entry, callback); 124 } 125 126 int OpenEntry(int64 key, Entry** entry, 127 const net::CompletionCallback& callback) { 128 int rv = owner_->disk_cache()->OpenEntry( 129 base::Int64ToString(key), &entry_ptr_, 130 base::Bind(&ActiveCall::OnAsyncCompletion, base::Unretained(this))); 131 return HandleImmediateReturnValue(rv, entry, callback); 132 } 133 134 int DoomEntry(int64 key, const net::CompletionCallback& callback) { 135 int rv = owner_->disk_cache()->DoomEntry( 136 base::Int64ToString(key), 137 base::Bind(&ActiveCall::OnAsyncCompletion, base::Unretained(this))); 138 return HandleImmediateReturnValue(rv, NULL, callback); 139 } 140 141 private: 142 int HandleImmediateReturnValue(int rv, Entry** entry, 143 const net::CompletionCallback& callback) { 144 if (rv == net::ERR_IO_PENDING) { 145 // OnAsyncCompletion will be called later. 146 callback_ = callback; 147 entry_ = entry; 148 owner_->AddActiveCall(this); 149 return net::ERR_IO_PENDING; 150 } 151 if (rv == net::OK && entry) 152 *entry = new EntryImpl(entry_ptr_, owner_); 153 delete this; 154 return rv; 155 } 156 157 void OnAsyncCompletion(int rv) { 158 owner_->RemoveActiveCall(this); 159 if (rv == net::OK && entry_) 160 *entry_ = new EntryImpl(entry_ptr_, owner_); 161 callback_.Run(rv); 162 callback_.Reset(); 163 delete this; 164 } 165 166 Entry** entry_; 167 net::CompletionCallback callback_; 168 AppCacheDiskCache* owner_; 169 disk_cache::Entry* entry_ptr_; 170 }; 171 172 AppCacheDiskCache::AppCacheDiskCache() 173 : is_disabled_(false) { 174 } 175 176 AppCacheDiskCache::~AppCacheDiskCache() { 177 Disable(); 178 } 179 180 int AppCacheDiskCache::InitWithDiskBackend( 181 const base::FilePath& disk_cache_directory, int disk_cache_size, bool force, 182 base::MessageLoopProxy* cache_thread, 183 const net::CompletionCallback& callback) { 184 return Init(net::APP_CACHE, disk_cache_directory, 185 disk_cache_size, force, cache_thread, callback); 186 } 187 188 int AppCacheDiskCache::InitWithMemBackend( 189 int mem_cache_size, const net::CompletionCallback& callback) { 190 return Init(net::MEMORY_CACHE, base::FilePath(), mem_cache_size, false, NULL, 191 callback); 192 } 193 194 void AppCacheDiskCache::Disable() { 195 if (is_disabled_) 196 return; 197 198 is_disabled_ = true; 199 200 if (create_backend_callback_.get()) { 201 create_backend_callback_->Cancel(); 202 create_backend_callback_ = NULL; 203 OnCreateBackendComplete(net::ERR_ABORTED); 204 } 205 206 // We need to close open file handles in order to reinitalize the 207 // appcache system on the fly. File handles held in both entries and in 208 // the main disk_cache::Backend class need to be released. 209 for (OpenEntries::const_iterator iter = open_entries_.begin(); 210 iter != open_entries_.end(); ++iter) { 211 (*iter)->Abandon(); 212 } 213 open_entries_.clear(); 214 disk_cache_.reset(); 215 STLDeleteElements(&active_calls_); 216 } 217 218 int AppCacheDiskCache::CreateEntry(int64 key, Entry** entry, 219 const net::CompletionCallback& callback) { 220 DCHECK(entry); 221 DCHECK(!callback.is_null()); 222 if (is_disabled_) 223 return net::ERR_ABORTED; 224 225 if (is_initializing()) { 226 pending_calls_.push_back(PendingCall(CREATE, key, entry, callback)); 227 return net::ERR_IO_PENDING; 228 } 229 230 if (!disk_cache_) 231 return net::ERR_FAILED; 232 233 return (new ActiveCall(this))->CreateEntry(key, entry, callback); 234 } 235 236 int AppCacheDiskCache::OpenEntry(int64 key, Entry** entry, 237 const net::CompletionCallback& callback) { 238 DCHECK(entry); 239 DCHECK(!callback.is_null()); 240 if (is_disabled_) 241 return net::ERR_ABORTED; 242 243 if (is_initializing()) { 244 pending_calls_.push_back(PendingCall(OPEN, key, entry, callback)); 245 return net::ERR_IO_PENDING; 246 } 247 248 if (!disk_cache_) 249 return net::ERR_FAILED; 250 251 return (new ActiveCall(this))->OpenEntry(key, entry, callback); 252 } 253 254 int AppCacheDiskCache::DoomEntry(int64 key, 255 const net::CompletionCallback& callback) { 256 DCHECK(!callback.is_null()); 257 if (is_disabled_) 258 return net::ERR_ABORTED; 259 260 if (is_initializing()) { 261 pending_calls_.push_back(PendingCall(DOOM, key, NULL, callback)); 262 return net::ERR_IO_PENDING; 263 } 264 265 if (!disk_cache_) 266 return net::ERR_FAILED; 267 268 return (new ActiveCall(this))->DoomEntry(key, callback); 269 } 270 271 AppCacheDiskCache::PendingCall::PendingCall() 272 : call_type(CREATE), 273 key(0), 274 entry(NULL) { 275 } 276 277 AppCacheDiskCache::PendingCall::PendingCall(PendingCallType call_type, 278 int64 key, 279 Entry** entry, 280 const net::CompletionCallback& callback) 281 : call_type(call_type), 282 key(key), 283 entry(entry), 284 callback(callback) { 285 } 286 287 AppCacheDiskCache::PendingCall::~PendingCall() {} 288 289 int AppCacheDiskCache::Init(net::CacheType cache_type, 290 const base::FilePath& cache_directory, 291 int cache_size, bool force, 292 base::MessageLoopProxy* cache_thread, 293 const net::CompletionCallback& callback) { 294 DCHECK(!is_initializing() && !disk_cache_.get()); 295 is_disabled_ = false; 296 create_backend_callback_ = new CreateBackendCallbackShim(this); 297 298 #if defined(APPCACHE_USE_SIMPLE_CACHE) 299 const net::BackendType backend_type = net::CACHE_BACKEND_SIMPLE; 300 #else 301 const net::BackendType backend_type = net::CACHE_BACKEND_DEFAULT; 302 #endif 303 int rv = disk_cache::CreateCacheBackend( 304 cache_type, backend_type, cache_directory, cache_size, 305 force, cache_thread, NULL, &(create_backend_callback_->backend_ptr_), 306 base::Bind(&CreateBackendCallbackShim::Callback, 307 create_backend_callback_)); 308 if (rv == net::ERR_IO_PENDING) 309 init_callback_ = callback; 310 else 311 OnCreateBackendComplete(rv); 312 return rv; 313 } 314 315 void AppCacheDiskCache::OnCreateBackendComplete(int rv) { 316 if (rv == net::OK) { 317 disk_cache_ = create_backend_callback_->backend_ptr_.Pass(); 318 } 319 create_backend_callback_ = NULL; 320 321 // Invoke our clients callback function. 322 if (!init_callback_.is_null()) { 323 init_callback_.Run(rv); 324 init_callback_.Reset(); 325 } 326 327 // Service pending calls that were queued up while we were initializing. 328 for (PendingCalls::const_iterator iter = pending_calls_.begin(); 329 iter < pending_calls_.end(); ++iter) { 330 int rv = net::ERR_FAILED; 331 switch (iter->call_type) { 332 case CREATE: 333 rv = CreateEntry(iter->key, iter->entry, iter->callback); 334 break; 335 case OPEN: 336 rv = OpenEntry(iter->key, iter->entry, iter->callback); 337 break; 338 case DOOM: 339 rv = DoomEntry(iter->key, iter->callback); 340 break; 341 default: 342 NOTREACHED(); 343 break; 344 } 345 if (rv != net::ERR_IO_PENDING) 346 iter->callback.Run(rv); 347 } 348 pending_calls_.clear(); 349 } 350 351 } // namespace appcache 352