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