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