• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
CreateBackendCallbackShim(AppCacheDiskCache * object)24   explicit CreateBackendCallbackShim(AppCacheDiskCache* object)
25       : appcache_diskcache_(object) {
26   }
27 
Cancel()28   void Cancel() {
29     appcache_diskcache_ = NULL;
30   }
31 
Callback(int rv)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 
~CreateBackendCallbackShim()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:
EntryImpl(disk_cache::Entry * disk_cache_entry)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.
Read(int index,int64 offset,net::IOBuffer * buf,int buf_len,const net::CompletionCallback & callback)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 
Write(int index,int64 offset,net::IOBuffer * buf,int buf_len,const net::CompletionCallback & callback)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 
GetSize(int index)75   virtual int64 GetSize(int index) OVERRIDE {
76     return disk_cache_entry_->GetDataSize(index);
77   }
78 
Close()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:
ActiveCall(AppCacheDiskCache * owner)92   explicit ActiveCall(AppCacheDiskCache* owner)
93       : entry_(NULL),
94         owner_(owner),
95         entry_ptr_(NULL) {
96   }
97 
CreateEntry(int64 key,Entry ** entry,const net::CompletionCallback & callback)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 
OpenEntry(int64 key,Entry ** entry,const net::CompletionCallback & callback)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 
DoomEntry(int64 key,const net::CompletionCallback & callback)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:
HandleImmediateReturnValue(int rv,Entry ** entry,const net::CompletionCallback & callback)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 
OnAsyncCompletion(int rv)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 
AppCacheDiskCache()152 AppCacheDiskCache::AppCacheDiskCache()
153     : is_disabled_(false) {
154 }
155 
~AppCacheDiskCache()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 
InitWithDiskBackend(const base::FilePath & disk_cache_directory,int disk_cache_size,bool force,base::MessageLoopProxy * cache_thread,const net::CompletionCallback & callback)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 
InitWithMemBackend(int mem_cache_size,const net::CompletionCallback & callback)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 
Disable()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 
CreateEntry(int64 key,Entry ** entry,const net::CompletionCallback & callback)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 
OpenEntry(int64 key,Entry ** entry,const net::CompletionCallback & callback)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 
DoomEntry(int64 key,const net::CompletionCallback & callback)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 
PendingCall()246 AppCacheDiskCache::PendingCall::PendingCall()
247     : call_type(CREATE),
248       key(0),
249       entry(NULL) {
250 }
251 
PendingCall(PendingCallType call_type,int64 key,Entry ** entry,const net::CompletionCallback & callback)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 
~PendingCall()262 AppCacheDiskCache::PendingCall::~PendingCall() {}
263 
Init(net::CacheType cache_type,const base::FilePath & cache_directory,int cache_size,bool force,base::MessageLoopProxy * cache_thread,const net::CompletionCallback & callback)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 
OnCreateBackendComplete(int rv)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