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 explicit EntryImpl(disk_cache::Entry
* disk_cache_entry
)
53 : disk_cache_entry_(disk_cache_entry
) {
54 DCHECK(disk_cache_entry
);
57 // Entry implementation.
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
);
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
);
75 virtual int64
GetSize(int index
) OVERRIDE
{
76 return disk_cache_entry_
->GetDataSize(index
);
79 virtual void Close() OVERRIDE
{
80 disk_cache_entry_
->Close();
85 disk_cache::Entry
* disk_cache_entry_
;
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
{
92 explicit ActiveCall(AppCacheDiskCache
* owner
)
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
);
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
);
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
);
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
;
128 owner_
->AddActiveCall(this);
129 return net::ERR_IO_PENDING
;
131 if (rv
== net::OK
&& entry
)
132 *entry
= new EntryImpl(entry_ptr_
);
137 void OnAsyncCompletion(int rv
) {
138 owner_
->RemoveActiveCall(this);
139 if (rv
== net::OK
&& entry_
)
140 *entry_
= new EntryImpl(entry_ptr_
);
147 net::CompletionCallback callback_
;
148 AppCacheDiskCache
* owner_
;
149 disk_cache::Entry
* entry_ptr_
;
152 AppCacheDiskCache::AppCacheDiskCache()
153 : is_disabled_(false) {
156 AppCacheDiskCache::~AppCacheDiskCache() {
157 if (create_backend_callback_
.get()) {
158 create_backend_callback_
->Cancel();
159 create_backend_callback_
= NULL
;
160 OnCreateBackendComplete(net::ERR_ABORTED
);
163 STLDeleteElements(&active_calls_
);
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
);
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
,
180 void AppCacheDiskCache::Disable() {
186 if (create_backend_callback_
.get()) {
187 create_backend_callback_
->Cancel();
188 create_backend_callback_
= NULL
;
189 OnCreateBackendComplete(net::ERR_ABORTED
);
193 int AppCacheDiskCache::CreateEntry(int64 key
, Entry
** entry
,
194 const net::CompletionCallback
& callback
) {
196 DCHECK(!callback
.is_null());
198 return net::ERR_ABORTED
;
200 if (is_initializing()) {
201 pending_calls_
.push_back(PendingCall(CREATE
, key
, entry
, callback
));
202 return net::ERR_IO_PENDING
;
206 return net::ERR_FAILED
;
208 return (new ActiveCall(this))->CreateEntry(key
, entry
, callback
);
211 int AppCacheDiskCache::OpenEntry(int64 key
, Entry
** entry
,
212 const net::CompletionCallback
& callback
) {
214 DCHECK(!callback
.is_null());
216 return net::ERR_ABORTED
;
218 if (is_initializing()) {
219 pending_calls_
.push_back(PendingCall(OPEN
, key
, entry
, callback
));
220 return net::ERR_IO_PENDING
;
224 return net::ERR_FAILED
;
226 return (new ActiveCall(this))->OpenEntry(key
, entry
, callback
);
229 int AppCacheDiskCache::DoomEntry(int64 key
,
230 const net::CompletionCallback
& callback
) {
231 DCHECK(!callback
.is_null());
233 return net::ERR_ABORTED
;
235 if (is_initializing()) {
236 pending_calls_
.push_back(PendingCall(DOOM
, key
, NULL
, callback
));
237 return net::ERR_IO_PENDING
;
241 return net::ERR_FAILED
;
243 return (new ActiveCall(this))->DoomEntry(key
, callback
);
246 AppCacheDiskCache::PendingCall::PendingCall()
252 AppCacheDiskCache::PendingCall::PendingCall(PendingCallType call_type
,
255 const net::CompletionCallback
& callback
)
256 : call_type(call_type
),
262 AppCacheDiskCache::PendingCall::~PendingCall() {}
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);
273 int rv
= disk_cache::CreateCacheBackend(
274 cache_type
, net::CACHE_BACKEND_DEFAULT
, cache_directory
, cache_size
,
275 force
, cache_thread
, NULL
, &(create_backend_callback_
->backend_ptr_
),
276 base::Bind(&CreateBackendCallbackShim::Callback
,
277 create_backend_callback_
));
278 if (rv
== net::ERR_IO_PENDING
)
279 init_callback_
= callback
;
281 OnCreateBackendComplete(rv
);
285 void AppCacheDiskCache::OnCreateBackendComplete(int rv
) {
287 disk_cache_
= create_backend_callback_
->backend_ptr_
.Pass();
289 create_backend_callback_
= NULL
;
291 // Invoke our clients callback function.
292 if (!init_callback_
.is_null()) {
293 init_callback_
.Run(rv
);
294 init_callback_
.Reset();
297 // Service pending calls that were queued up while we were initializing.
298 for (PendingCalls::const_iterator iter
= pending_calls_
.begin();
299 iter
< pending_calls_
.end(); ++iter
) {
300 int rv
= net::ERR_FAILED
;
301 switch (iter
->call_type
) {
303 rv
= CreateEntry(iter
->key
, iter
->entry
, iter
->callback
);
306 rv
= OpenEntry(iter
->key
, iter
->entry
, iter
->callback
);
309 rv
= DoomEntry(iter
->key
, iter
->callback
);
315 if (rv
!= net::ERR_IO_PENDING
)
316 iter
->callback
.Run(rv
);
318 pending_calls_
.clear();
321 } // namespace appcache