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 "content/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/single_thread_task_runner.h"
12 #include "base/stl_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "net/base/cache_type.h"
15 #include "net/base/net_errors.h"
19 // A callback shim that provides storage for the 'backend_ptr' value
20 // and will delete a resulting ptr if completion occurs after its
22 class AppCacheDiskCache::CreateBackendCallbackShim
23 : public base::RefCounted
<CreateBackendCallbackShim
> {
25 explicit CreateBackendCallbackShim(AppCacheDiskCache
* object
)
26 : appcache_diskcache_(object
) {
30 appcache_diskcache_
= NULL
;
33 void Callback(int rv
) {
34 if (appcache_diskcache_
)
35 appcache_diskcache_
->OnCreateBackendComplete(rv
);
38 scoped_ptr
<disk_cache::Backend
> backend_ptr_
; // Accessed directly.
41 friend class base::RefCounted
<CreateBackendCallbackShim
>;
43 ~CreateBackendCallbackShim() {
46 AppCacheDiskCache
* appcache_diskcache_
; // Unowned pointer.
49 // An implementation of AppCacheDiskCacheInterface::Entry that's a thin
50 // wrapper around disk_cache::Entry.
51 class AppCacheDiskCache::EntryImpl
: public Entry
{
53 EntryImpl(disk_cache::Entry
* disk_cache_entry
,
54 AppCacheDiskCache
* owner
)
55 : disk_cache_entry_(disk_cache_entry
), owner_(owner
) {
56 DCHECK(disk_cache_entry
);
58 owner_
->AddOpenEntry(this);
61 // Entry implementation.
62 virtual int Read(int index
, int64 offset
, net::IOBuffer
* buf
, int buf_len
,
63 const net::CompletionCallback
& callback
) OVERRIDE
{
64 if (offset
< 0 || offset
> kint32max
)
65 return net::ERR_INVALID_ARGUMENT
;
66 if (!disk_cache_entry_
)
67 return net::ERR_ABORTED
;
68 return disk_cache_entry_
->ReadData(
69 index
, static_cast<int>(offset
), buf
, buf_len
, callback
);
72 virtual int Write(int index
, int64 offset
, net::IOBuffer
* buf
, int buf_len
,
73 const net::CompletionCallback
& callback
) OVERRIDE
{
74 if (offset
< 0 || offset
> kint32max
)
75 return net::ERR_INVALID_ARGUMENT
;
76 if (!disk_cache_entry_
)
77 return net::ERR_ABORTED
;
78 const bool kTruncate
= true;
79 return disk_cache_entry_
->WriteData(
80 index
, static_cast<int>(offset
), buf
, buf_len
, callback
, kTruncate
);
83 virtual int64
GetSize(int index
) OVERRIDE
{
84 return disk_cache_entry_
? disk_cache_entry_
->GetDataSize(index
) : 0L;
87 virtual void Close() OVERRIDE
{
88 if (disk_cache_entry_
)
89 disk_cache_entry_
->Close();
95 disk_cache_entry_
->Close();
96 disk_cache_entry_
= NULL
;
100 virtual ~EntryImpl() {
102 owner_
->RemoveOpenEntry(this);
105 disk_cache::Entry
* disk_cache_entry_
;
106 AppCacheDiskCache
* owner_
;
109 // Separate object to hold state for each Create, Delete, or Doom call
110 // while the call is in-flight and to produce an EntryImpl upon completion.
111 class AppCacheDiskCache::ActiveCall
{
113 explicit ActiveCall(AppCacheDiskCache
* owner
)
119 int CreateEntry(int64 key
, Entry
** entry
,
120 const net::CompletionCallback
& callback
) {
121 int rv
= owner_
->disk_cache()->CreateEntry(
122 base::Int64ToString(key
), &entry_ptr_
,
123 base::Bind(&ActiveCall::OnAsyncCompletion
, base::Unretained(this)));
124 return HandleImmediateReturnValue(rv
, entry
, callback
);
127 int OpenEntry(int64 key
, Entry
** entry
,
128 const net::CompletionCallback
& callback
) {
129 int rv
= owner_
->disk_cache()->OpenEntry(
130 base::Int64ToString(key
), &entry_ptr_
,
131 base::Bind(&ActiveCall::OnAsyncCompletion
, base::Unretained(this)));
132 return HandleImmediateReturnValue(rv
, entry
, callback
);
135 int DoomEntry(int64 key
, const net::CompletionCallback
& callback
) {
136 int rv
= owner_
->disk_cache()->DoomEntry(
137 base::Int64ToString(key
),
138 base::Bind(&ActiveCall::OnAsyncCompletion
, base::Unretained(this)));
139 return HandleImmediateReturnValue(rv
, NULL
, callback
);
143 int HandleImmediateReturnValue(int rv
, Entry
** entry
,
144 const net::CompletionCallback
& callback
) {
145 if (rv
== net::ERR_IO_PENDING
) {
146 // OnAsyncCompletion will be called later.
147 callback_
= callback
;
149 owner_
->AddActiveCall(this);
150 return net::ERR_IO_PENDING
;
152 if (rv
== net::OK
&& entry
)
153 *entry
= new EntryImpl(entry_ptr_
, owner_
);
158 void OnAsyncCompletion(int rv
) {
159 owner_
->RemoveActiveCall(this);
160 if (rv
== net::OK
&& entry_
)
161 *entry_
= new EntryImpl(entry_ptr_
, owner_
);
168 net::CompletionCallback callback_
;
169 AppCacheDiskCache
* owner_
;
170 disk_cache::Entry
* entry_ptr_
;
173 AppCacheDiskCache::AppCacheDiskCache()
174 : is_disabled_(false) {
177 AppCacheDiskCache::~AppCacheDiskCache() {
181 int AppCacheDiskCache::InitWithDiskBackend(
182 const base::FilePath
& disk_cache_directory
,
185 const scoped_refptr
<base::SingleThreadTaskRunner
>& cache_thread
,
186 const net::CompletionCallback
& callback
) {
187 return Init(net::APP_CACHE
,
188 disk_cache_directory
,
195 int AppCacheDiskCache::InitWithMemBackend(
196 int mem_cache_size
, const net::CompletionCallback
& callback
) {
197 return Init(net::MEMORY_CACHE
, base::FilePath(), mem_cache_size
, false, NULL
,
201 void AppCacheDiskCache::Disable() {
207 if (create_backend_callback_
.get()) {
208 create_backend_callback_
->Cancel();
209 create_backend_callback_
= NULL
;
210 OnCreateBackendComplete(net::ERR_ABORTED
);
213 // We need to close open file handles in order to reinitalize the
214 // appcache system on the fly. File handles held in both entries and in
215 // the main disk_cache::Backend class need to be released.
216 for (OpenEntries::const_iterator iter
= open_entries_
.begin();
217 iter
!= open_entries_
.end(); ++iter
) {
220 open_entries_
.clear();
222 STLDeleteElements(&active_calls_
);
225 int AppCacheDiskCache::CreateEntry(int64 key
, Entry
** entry
,
226 const net::CompletionCallback
& callback
) {
228 DCHECK(!callback
.is_null());
230 return net::ERR_ABORTED
;
232 if (is_initializing()) {
233 pending_calls_
.push_back(PendingCall(CREATE
, key
, entry
, callback
));
234 return net::ERR_IO_PENDING
;
238 return net::ERR_FAILED
;
240 return (new ActiveCall(this))->CreateEntry(key
, entry
, callback
);
243 int AppCacheDiskCache::OpenEntry(int64 key
, Entry
** entry
,
244 const net::CompletionCallback
& callback
) {
246 DCHECK(!callback
.is_null());
248 return net::ERR_ABORTED
;
250 if (is_initializing()) {
251 pending_calls_
.push_back(PendingCall(OPEN
, key
, entry
, callback
));
252 return net::ERR_IO_PENDING
;
256 return net::ERR_FAILED
;
258 return (new ActiveCall(this))->OpenEntry(key
, entry
, callback
);
261 int AppCacheDiskCache::DoomEntry(int64 key
,
262 const net::CompletionCallback
& callback
) {
263 DCHECK(!callback
.is_null());
265 return net::ERR_ABORTED
;
267 if (is_initializing()) {
268 pending_calls_
.push_back(PendingCall(DOOM
, key
, NULL
, callback
));
269 return net::ERR_IO_PENDING
;
273 return net::ERR_FAILED
;
275 return (new ActiveCall(this))->DoomEntry(key
, callback
);
278 AppCacheDiskCache::PendingCall::PendingCall()
284 AppCacheDiskCache::PendingCall::PendingCall(PendingCallType call_type
,
287 const net::CompletionCallback
& callback
)
288 : call_type(call_type
),
294 AppCacheDiskCache::PendingCall::~PendingCall() {}
296 int AppCacheDiskCache::Init(
297 net::CacheType cache_type
,
298 const base::FilePath
& cache_directory
,
301 const scoped_refptr
<base::SingleThreadTaskRunner
>& cache_thread
,
302 const net::CompletionCallback
& callback
) {
303 DCHECK(!is_initializing() && !disk_cache_
.get());
304 is_disabled_
= false;
305 create_backend_callback_
= new CreateBackendCallbackShim(this);
307 #if defined(APPCACHE_USE_SIMPLE_CACHE)
308 const net::BackendType backend_type
= net::CACHE_BACKEND_SIMPLE
;
310 const net::BackendType backend_type
= net::CACHE_BACKEND_DEFAULT
;
312 int rv
= disk_cache::CreateCacheBackend(
320 &(create_backend_callback_
->backend_ptr_
),
321 base::Bind(&CreateBackendCallbackShim::Callback
,
322 create_backend_callback_
));
323 if (rv
== net::ERR_IO_PENDING
)
324 init_callback_
= callback
;
326 OnCreateBackendComplete(rv
);
330 void AppCacheDiskCache::OnCreateBackendComplete(int rv
) {
332 disk_cache_
= create_backend_callback_
->backend_ptr_
.Pass();
334 create_backend_callback_
= NULL
;
336 // Invoke our clients callback function.
337 if (!init_callback_
.is_null()) {
338 init_callback_
.Run(rv
);
339 init_callback_
.Reset();
342 // Service pending calls that were queued up while we were initializing.
343 for (PendingCalls::const_iterator iter
= pending_calls_
.begin();
344 iter
< pending_calls_
.end(); ++iter
) {
345 int rv
= net::ERR_FAILED
;
346 switch (iter
->call_type
) {
348 rv
= CreateEntry(iter
->key
, iter
->entry
, iter
->callback
);
351 rv
= OpenEntry(iter
->key
, iter
->entry
, iter
->callback
);
354 rv
= DoomEntry(iter
->key
, iter
->callback
);
360 if (rv
!= net::ERR_IO_PENDING
)
361 iter
->callback
.Run(rv
);
363 pending_calls_
.clear();
366 } // namespace content