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.
5 #include "webkit/browser/appcache/appcache_disk_cache.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"
18 // A callback shim that provides storage for the 'backend_ptr' value
19 // and will delete a resulting ptr if completion occurs after its
21 class AppCacheDiskCache::CreateBackendCallbackShim
22 : public base::RefCounted
<CreateBackendCallbackShim
> {
24 explicit CreateBackendCallbackShim(AppCacheDiskCache
* object
)
25 : appcache_diskcache_(object
) {
29 appcache_diskcache_
= NULL
;
32 void Callback(int rv
) {
33 if (appcache_diskcache_
)
34 appcache_diskcache_
->OnCreateBackendComplete(rv
);
37 scoped_ptr
<disk_cache::Backend
> backend_ptr_
; // Accessed directly.
40 friend class base::RefCounted
<CreateBackendCallbackShim
>;
42 ~CreateBackendCallbackShim() {
45 AppCacheDiskCache
* appcache_diskcache_
; // Unowned pointer.
48 // An implementation of AppCacheDiskCacheInterface::Entry that's a thin
49 // wrapper around disk_cache::Entry.
50 class AppCacheDiskCache::EntryImpl
: public Entry
{
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
);
57 owner_
->AddOpenEntry(this);
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
);
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
);
82 virtual int64
GetSize(int index
) OVERRIDE
{
83 return disk_cache_entry_
? disk_cache_entry_
->GetDataSize(index
) : 0L;
86 virtual void Close() OVERRIDE
{
87 if (disk_cache_entry_
)
88 disk_cache_entry_
->Close();
94 disk_cache_entry_
->Close();
95 disk_cache_entry_
= NULL
;
99 virtual ~EntryImpl() {
101 owner_
->RemoveOpenEntry(this);
104 disk_cache::Entry
* disk_cache_entry_
;
105 AppCacheDiskCache
* owner_
;
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
{
112 explicit ActiveCall(AppCacheDiskCache
* owner
)
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
);
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
);
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
);
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
;
148 owner_
->AddActiveCall(this);
149 return net::ERR_IO_PENDING
;
151 if (rv
== net::OK
&& entry
)
152 *entry
= new EntryImpl(entry_ptr_
, owner_
);
157 void OnAsyncCompletion(int rv
) {
158 owner_
->RemoveActiveCall(this);
159 if (rv
== net::OK
&& entry_
)
160 *entry_
= new EntryImpl(entry_ptr_
, owner_
);
167 net::CompletionCallback callback_
;
168 AppCacheDiskCache
* owner_
;
169 disk_cache::Entry
* entry_ptr_
;
172 AppCacheDiskCache::AppCacheDiskCache()
173 : is_disabled_(false) {
176 AppCacheDiskCache::~AppCacheDiskCache() {
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
);
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
,
194 void AppCacheDiskCache::Disable() {
200 if (create_backend_callback_
.get()) {
201 create_backend_callback_
->Cancel();
202 create_backend_callback_
= NULL
;
203 OnCreateBackendComplete(net::ERR_ABORTED
);
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
) {
213 open_entries_
.clear();
215 STLDeleteElements(&active_calls_
);
218 int AppCacheDiskCache::CreateEntry(int64 key
, Entry
** entry
,
219 const net::CompletionCallback
& callback
) {
221 DCHECK(!callback
.is_null());
223 return net::ERR_ABORTED
;
225 if (is_initializing()) {
226 pending_calls_
.push_back(PendingCall(CREATE
, key
, entry
, callback
));
227 return net::ERR_IO_PENDING
;
231 return net::ERR_FAILED
;
233 return (new ActiveCall(this))->CreateEntry(key
, entry
, callback
);
236 int AppCacheDiskCache::OpenEntry(int64 key
, Entry
** entry
,
237 const net::CompletionCallback
& callback
) {
239 DCHECK(!callback
.is_null());
241 return net::ERR_ABORTED
;
243 if (is_initializing()) {
244 pending_calls_
.push_back(PendingCall(OPEN
, key
, entry
, callback
));
245 return net::ERR_IO_PENDING
;
249 return net::ERR_FAILED
;
251 return (new ActiveCall(this))->OpenEntry(key
, entry
, callback
);
254 int AppCacheDiskCache::DoomEntry(int64 key
,
255 const net::CompletionCallback
& callback
) {
256 DCHECK(!callback
.is_null());
258 return net::ERR_ABORTED
;
260 if (is_initializing()) {
261 pending_calls_
.push_back(PendingCall(DOOM
, key
, NULL
, callback
));
262 return net::ERR_IO_PENDING
;
266 return net::ERR_FAILED
;
268 return (new ActiveCall(this))->DoomEntry(key
, callback
);
271 AppCacheDiskCache::PendingCall::PendingCall()
277 AppCacheDiskCache::PendingCall::PendingCall(PendingCallType call_type
,
280 const net::CompletionCallback
& callback
)
281 : call_type(call_type
),
287 AppCacheDiskCache::PendingCall::~PendingCall() {}
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);
298 #if defined(APPCACHE_USE_SIMPLE_CACHE)
299 const net::BackendType backend_type
= net::CACHE_BACKEND_SIMPLE
;
301 const net::BackendType backend_type
= net::CACHE_BACKEND_DEFAULT
;
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
;
311 OnCreateBackendComplete(rv
);
315 void AppCacheDiskCache::OnCreateBackendComplete(int rv
) {
317 disk_cache_
= create_backend_callback_
->backend_ptr_
.Pass();
319 create_backend_callback_
= NULL
;
321 // Invoke our clients callback function.
322 if (!init_callback_
.is_null()) {
323 init_callback_
.Run(rv
);
324 init_callback_
.Reset();
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
) {
333 rv
= CreateEntry(iter
->key
, iter
->entry
, iter
->callback
);
336 rv
= OpenEntry(iter
->key
, iter
->entry
, iter
->callback
);
339 rv
= DoomEntry(iter
->key
, iter
->callback
);
345 if (rv
!= net::ERR_IO_PENDING
)
346 iter
->callback
.Run(rv
);
348 pending_calls_
.clear();
351 } // namespace appcache