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 explicit EntryImpl(disk_cache::Entry* disk_cache_entry) 53 : disk_cache_entry_(disk_cache_entry) { 54 DCHECK(disk_cache_entry); 55 } 56 57 // Entry implementation. 58 virtual int Read(int index, int64 offset, net::IOBuffer* buf, int buf_len, 59 const net::CompletionCallback& callback) OVERRIDE { 60 if (offset < 0 || offset > kint32max) 61 return net::ERR_INVALID_ARGUMENT; 62 return disk_cache_entry_->ReadData( 63 index, static_cast<int>(offset), buf, buf_len, callback); 64 } 65 66 virtual int Write(int index, int64 offset, net::IOBuffer* buf, int buf_len, 67 const net::CompletionCallback& callback) OVERRIDE { 68 if (offset < 0 || offset > kint32max) 69 return net::ERR_INVALID_ARGUMENT; 70 const bool kTruncate = true; 71 return disk_cache_entry_->WriteData( 72 index, static_cast<int>(offset), buf, buf_len, callback, kTruncate); 73 } 74 75 virtual int64 GetSize(int index) OVERRIDE { 76 return disk_cache_entry_->GetDataSize(index); 77 } 78 79 virtual void Close() OVERRIDE { 80 disk_cache_entry_->Close(); 81 delete this; 82 } 83 84 private: 85 disk_cache::Entry* disk_cache_entry_; 86 }; 87 88 // Separate object to hold state for each Create, Delete, or Doom call 89 // while the call is in-flight and to produce an EntryImpl upon completion. 90 class AppCacheDiskCache::ActiveCall { 91 public: 92 explicit ActiveCall(AppCacheDiskCache* owner) 93 : entry_(NULL), 94 owner_(owner), 95 entry_ptr_(NULL) { 96 } 97 98 int CreateEntry(int64 key, Entry** entry, 99 const net::CompletionCallback& callback) { 100 int rv = owner_->disk_cache()->CreateEntry( 101 base::Int64ToString(key), &entry_ptr_, 102 base::Bind(&ActiveCall::OnAsyncCompletion, base::Unretained(this))); 103 return HandleImmediateReturnValue(rv, entry, callback); 104 } 105 106 int OpenEntry(int64 key, Entry** entry, 107 const net::CompletionCallback& callback) { 108 int rv = owner_->disk_cache()->OpenEntry( 109 base::Int64ToString(key), &entry_ptr_, 110 base::Bind(&ActiveCall::OnAsyncCompletion, base::Unretained(this))); 111 return HandleImmediateReturnValue(rv, entry, callback); 112 } 113 114 int DoomEntry(int64 key, const net::CompletionCallback& callback) { 115 int rv = owner_->disk_cache()->DoomEntry( 116 base::Int64ToString(key), 117 base::Bind(&ActiveCall::OnAsyncCompletion, base::Unretained(this))); 118 return HandleImmediateReturnValue(rv, NULL, callback); 119 } 120 121 private: 122 int HandleImmediateReturnValue(int rv, Entry** entry, 123 const net::CompletionCallback& callback) { 124 if (rv == net::ERR_IO_PENDING) { 125 // OnAsyncCompletion will be called later. 126 callback_ = callback; 127 entry_ = entry; 128 owner_->AddActiveCall(this); 129 return net::ERR_IO_PENDING; 130 } 131 if (rv == net::OK && entry) 132 *entry = new EntryImpl(entry_ptr_); 133 delete this; 134 return rv; 135 } 136 137 void OnAsyncCompletion(int rv) { 138 owner_->RemoveActiveCall(this); 139 if (rv == net::OK && entry_) 140 *entry_ = new EntryImpl(entry_ptr_); 141 callback_.Run(rv); 142 callback_.Reset(); 143 delete this; 144 } 145 146 Entry** entry_; 147 net::CompletionCallback callback_; 148 AppCacheDiskCache* owner_; 149 disk_cache::Entry* entry_ptr_; 150 }; 151 152 AppCacheDiskCache::AppCacheDiskCache() 153 : is_disabled_(false) { 154 } 155 156 AppCacheDiskCache::~AppCacheDiskCache() { 157 if (create_backend_callback_.get()) { 158 create_backend_callback_->Cancel(); 159 create_backend_callback_ = NULL; 160 OnCreateBackendComplete(net::ERR_ABORTED); 161 } 162 disk_cache_.reset(); 163 STLDeleteElements(&active_calls_); 164 } 165 166 int AppCacheDiskCache::InitWithDiskBackend( 167 const base::FilePath& disk_cache_directory, int disk_cache_size, bool force, 168 base::MessageLoopProxy* cache_thread, 169 const net::CompletionCallback& callback) { 170 return Init(net::APP_CACHE, disk_cache_directory, 171 disk_cache_size, force, cache_thread, callback); 172 } 173 174 int AppCacheDiskCache::InitWithMemBackend( 175 int mem_cache_size, const net::CompletionCallback& callback) { 176 return Init(net::MEMORY_CACHE, base::FilePath(), mem_cache_size, false, NULL, 177 callback); 178 } 179 180 void AppCacheDiskCache::Disable() { 181 if (is_disabled_) 182 return; 183 184 is_disabled_ = true; 185 186 if (create_backend_callback_.get()) { 187 create_backend_callback_->Cancel(); 188 create_backend_callback_ = NULL; 189 OnCreateBackendComplete(net::ERR_ABORTED); 190 } 191 } 192 193 int AppCacheDiskCache::CreateEntry(int64 key, Entry** entry, 194 const net::CompletionCallback& callback) { 195 DCHECK(entry); 196 DCHECK(!callback.is_null()); 197 if (is_disabled_) 198 return net::ERR_ABORTED; 199 200 if (is_initializing()) { 201 pending_calls_.push_back(PendingCall(CREATE, key, entry, callback)); 202 return net::ERR_IO_PENDING; 203 } 204 205 if (!disk_cache_) 206 return net::ERR_FAILED; 207 208 return (new ActiveCall(this))->CreateEntry(key, entry, callback); 209 } 210 211 int AppCacheDiskCache::OpenEntry(int64 key, Entry** entry, 212 const net::CompletionCallback& callback) { 213 DCHECK(entry); 214 DCHECK(!callback.is_null()); 215 if (is_disabled_) 216 return net::ERR_ABORTED; 217 218 if (is_initializing()) { 219 pending_calls_.push_back(PendingCall(OPEN, key, entry, callback)); 220 return net::ERR_IO_PENDING; 221 } 222 223 if (!disk_cache_) 224 return net::ERR_FAILED; 225 226 return (new ActiveCall(this))->OpenEntry(key, entry, callback); 227 } 228 229 int AppCacheDiskCache::DoomEntry(int64 key, 230 const net::CompletionCallback& callback) { 231 DCHECK(!callback.is_null()); 232 if (is_disabled_) 233 return net::ERR_ABORTED; 234 235 if (is_initializing()) { 236 pending_calls_.push_back(PendingCall(DOOM, key, NULL, callback)); 237 return net::ERR_IO_PENDING; 238 } 239 240 if (!disk_cache_) 241 return net::ERR_FAILED; 242 243 return (new ActiveCall(this))->DoomEntry(key, callback); 244 } 245 246 AppCacheDiskCache::PendingCall::PendingCall() 247 : call_type(CREATE), 248 key(0), 249 entry(NULL) { 250 } 251 252 AppCacheDiskCache::PendingCall::PendingCall(PendingCallType call_type, 253 int64 key, 254 Entry** entry, 255 const net::CompletionCallback& callback) 256 : call_type(call_type), 257 key(key), 258 entry(entry), 259 callback(callback) { 260 } 261 262 AppCacheDiskCache::PendingCall::~PendingCall() {} 263 264 int AppCacheDiskCache::Init(net::CacheType cache_type, 265 const base::FilePath& cache_directory, 266 int cache_size, bool force, 267 base::MessageLoopProxy* cache_thread, 268 const net::CompletionCallback& callback) { 269 DCHECK(!is_initializing() && !disk_cache_.get()); 270 is_disabled_ = false; 271 create_backend_callback_ = new CreateBackendCallbackShim(this); 272 273 #if defined(APPCACHE_USE_SIMPLE_CACHE) 274 const net::BackendType backend_type = net::CACHE_BACKEND_SIMPLE; 275 #else 276 const net::BackendType backend_type = net::CACHE_BACKEND_DEFAULT; 277 #endif 278 int rv = disk_cache::CreateCacheBackend( 279 cache_type, backend_type, cache_directory, cache_size, 280 force, cache_thread, NULL, &(create_backend_callback_->backend_ptr_), 281 base::Bind(&CreateBackendCallbackShim::Callback, 282 create_backend_callback_)); 283 if (rv == net::ERR_IO_PENDING) 284 init_callback_ = callback; 285 else 286 OnCreateBackendComplete(rv); 287 return rv; 288 } 289 290 void AppCacheDiskCache::OnCreateBackendComplete(int rv) { 291 if (rv == net::OK) { 292 disk_cache_ = create_backend_callback_->backend_ptr_.Pass(); 293 } 294 create_backend_callback_ = NULL; 295 296 // Invoke our clients callback function. 297 if (!init_callback_.is_null()) { 298 init_callback_.Run(rv); 299 init_callback_.Reset(); 300 } 301 302 // Service pending calls that were queued up while we were initializing. 303 for (PendingCalls::const_iterator iter = pending_calls_.begin(); 304 iter < pending_calls_.end(); ++iter) { 305 int rv = net::ERR_FAILED; 306 switch (iter->call_type) { 307 case CREATE: 308 rv = CreateEntry(iter->key, iter->entry, iter->callback); 309 break; 310 case OPEN: 311 rv = OpenEntry(iter->key, iter->entry, iter->callback); 312 break; 313 case DOOM: 314 rv = DoomEntry(iter->key, iter->callback); 315 break; 316 default: 317 NOTREACHED(); 318 break; 319 } 320 if (rv != net::ERR_IO_PENDING) 321 iter->callback.Run(rv); 322 } 323 pending_calls_.clear(); 324 } 325 326 } // namespace appcache 327