Home | History | Annotate | Download | only in appcache
      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   int rv = disk_cache::CreateCacheBackend(
    274       cache_type, net::CACHE_BACKEND_DEFAULT, cache_directory, cache_size,
    275       force, cache_thread, NULL, &(create_backend_callback_->backend_ptr_),
    276       base::Bind(&CreateBackendCallbackShim::Callback,
    277                  create_backend_callback_));
    278   if (rv == net::ERR_IO_PENDING)
    279     init_callback_ = callback;
    280   else
    281     OnCreateBackendComplete(rv);
    282   return rv;
    283 }
    284 
    285 void AppCacheDiskCache::OnCreateBackendComplete(int rv) {
    286   if (rv == net::OK) {
    287     disk_cache_ = create_backend_callback_->backend_ptr_.Pass();
    288   }
    289   create_backend_callback_ = NULL;
    290 
    291   // Invoke our clients callback function.
    292   if (!init_callback_.is_null()) {
    293     init_callback_.Run(rv);
    294     init_callback_.Reset();
    295   }
    296 
    297   // Service pending calls that were queued up while we were initializing.
    298   for (PendingCalls::const_iterator iter = pending_calls_.begin();
    299        iter < pending_calls_.end(); ++iter) {
    300     int rv = net::ERR_FAILED;
    301     switch (iter->call_type) {
    302       case CREATE:
    303         rv = CreateEntry(iter->key, iter->entry, iter->callback);
    304         break;
    305       case OPEN:
    306         rv = OpenEntry(iter->key, iter->entry, iter->callback);
    307         break;
    308       case DOOM:
    309         rv = DoomEntry(iter->key, iter->callback);
    310         break;
    311       default:
    312         NOTREACHED();
    313         break;
    314     }
    315     if (rv != net::ERR_IO_PENDING)
    316       iter->callback.Run(rv);
    317   }
    318   pending_calls_.clear();
    319 }
    320 
    321 }  // namespace appcache
    322