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 "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