| OLD | NEW |
| (Empty) |
| 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 | |
| OLD | NEW |