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