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.
66 const net::CompletionCallback
& callback
) override
{
67 if (offset
< 0 || offset
> kint32max
)
68 return net::ERR_INVALID_ARGUMENT
;
69 if (!disk_cache_entry_
)
70 return net::ERR_ABORTED
;
71 return disk_cache_entry_
->ReadData(
72 index
, static_cast<int>(offset
), buf
, buf_len
, callback
);
79 const net::CompletionCallback
& callback
) override
{
80 if (offset
< 0 || offset
> kint32max
)
81 return net::ERR_INVALID_ARGUMENT
;
82 if (!disk_cache_entry_
)
83 return net::ERR_ABORTED
;
84 const bool kTruncate
= true;
85 return disk_cache_entry_
->WriteData(
86 index
, static_cast<int>(offset
), buf
, buf_len
, callback
, kTruncate
);
89 int64
GetSize(int index
) override
{
90 return disk_cache_entry_
? disk_cache_entry_
->GetDataSize(index
) : 0L;
93 void Close() override
{
94 if (disk_cache_entry_
)
95 disk_cache_entry_
->Close();
101 disk_cache_entry_
->Close();
102 disk_cache_entry_
= NULL
;
106 ~EntryImpl() override
{
108 owner_
->RemoveOpenEntry(this);
111 disk_cache::Entry
* disk_cache_entry_
;
112 AppCacheDiskCache
* owner_
;
115 // Separate object to hold state for each Create, Delete, or Doom call
116 // while the call is in-flight and to produce an EntryImpl upon completion.
117 class AppCacheDiskCache::ActiveCall
{
119 explicit ActiveCall(AppCacheDiskCache
* owner
)
125 int CreateEntry(int64 key
, Entry
** entry
,
126 const net::CompletionCallback
& callback
) {
127 int rv
= owner_
->disk_cache()->CreateEntry(
128 base::Int64ToString(key
), &entry_ptr_
,
129 base::Bind(&ActiveCall::OnAsyncCompletion
, base::Unretained(this)));
130 return HandleImmediateReturnValue(rv
, entry
, callback
);
133 int OpenEntry(int64 key
, Entry
** entry
,
134 const net::CompletionCallback
& callback
) {
135 int rv
= owner_
->disk_cache()->OpenEntry(
136 base::Int64ToString(key
), &entry_ptr_
,
137 base::Bind(&ActiveCall::OnAsyncCompletion
, base::Unretained(this)));
138 return HandleImmediateReturnValue(rv
, entry
, callback
);
141 int DoomEntry(int64 key
, const net::CompletionCallback
& callback
) {
142 int rv
= owner_
->disk_cache()->DoomEntry(
143 base::Int64ToString(key
),
144 base::Bind(&ActiveCall::OnAsyncCompletion
, base::Unretained(this)));
145 return HandleImmediateReturnValue(rv
, NULL
, callback
);
149 int HandleImmediateReturnValue(int rv
, Entry
** entry
,
150 const net::CompletionCallback
& callback
) {
151 if (rv
== net::ERR_IO_PENDING
) {
152 // OnAsyncCompletion will be called later.
153 callback_
= callback
;
155 owner_
->AddActiveCall(this);
156 return net::ERR_IO_PENDING
;
158 if (rv
== net::OK
&& entry
)
159 *entry
= new EntryImpl(entry_ptr_
, owner_
);
164 void OnAsyncCompletion(int rv
) {
165 owner_
->RemoveActiveCall(this);
166 if (rv
== net::OK
&& entry_
)
167 *entry_
= new EntryImpl(entry_ptr_
, owner_
);
174 net::CompletionCallback callback_
;
175 AppCacheDiskCache
* owner_
;
176 disk_cache::Entry
* entry_ptr_
;
179 AppCacheDiskCache::AppCacheDiskCache()
180 : is_disabled_(false) {
183 AppCacheDiskCache::~AppCacheDiskCache() {
187 int AppCacheDiskCache::InitWithDiskBackend(
188 const base::FilePath
& disk_cache_directory
,
191 const scoped_refptr
<base::SingleThreadTaskRunner
>& cache_thread
,
192 const net::CompletionCallback
& callback
) {
193 return Init(net::APP_CACHE
,
194 disk_cache_directory
,
201 int AppCacheDiskCache::InitWithMemBackend(
202 int mem_cache_size
, const net::CompletionCallback
& callback
) {
203 return Init(net::MEMORY_CACHE
, base::FilePath(), mem_cache_size
, false, NULL
,
207 void AppCacheDiskCache::Disable() {
213 if (create_backend_callback_
.get()) {
214 create_backend_callback_
->Cancel();
215 create_backend_callback_
= NULL
;
216 OnCreateBackendComplete(net::ERR_ABORTED
);
219 // We need to close open file handles in order to reinitalize the
220 // appcache system on the fly. File handles held in both entries and in
221 // the main disk_cache::Backend class need to be released.
222 for (OpenEntries::const_iterator iter
= open_entries_
.begin();
223 iter
!= open_entries_
.end(); ++iter
) {
226 open_entries_
.clear();
228 STLDeleteElements(&active_calls_
);
231 int AppCacheDiskCache::CreateEntry(int64 key
, Entry
** entry
,
232 const net::CompletionCallback
& callback
) {
234 DCHECK(!callback
.is_null());
236 return net::ERR_ABORTED
;
238 if (is_initializing()) {
239 pending_calls_
.push_back(PendingCall(CREATE
, key
, entry
, callback
));
240 return net::ERR_IO_PENDING
;
244 return net::ERR_FAILED
;
246 return (new ActiveCall(this))->CreateEntry(key
, entry
, callback
);
249 int AppCacheDiskCache::OpenEntry(int64 key
, Entry
** entry
,
250 const net::CompletionCallback
& callback
) {
252 DCHECK(!callback
.is_null());
254 return net::ERR_ABORTED
;
256 if (is_initializing()) {
257 pending_calls_
.push_back(PendingCall(OPEN
, key
, entry
, callback
));
258 return net::ERR_IO_PENDING
;
262 return net::ERR_FAILED
;
264 return (new ActiveCall(this))->OpenEntry(key
, entry
, callback
);
267 int AppCacheDiskCache::DoomEntry(int64 key
,
268 const net::CompletionCallback
& callback
) {
269 DCHECK(!callback
.is_null());
271 return net::ERR_ABORTED
;
273 if (is_initializing()) {
274 pending_calls_
.push_back(PendingCall(DOOM
, key
, NULL
, callback
));
275 return net::ERR_IO_PENDING
;
279 return net::ERR_FAILED
;
281 return (new ActiveCall(this))->DoomEntry(key
, callback
);
284 AppCacheDiskCache::PendingCall::PendingCall()
290 AppCacheDiskCache::PendingCall::PendingCall(PendingCallType call_type
,
293 const net::CompletionCallback
& callback
)
294 : call_type(call_type
),
300 AppCacheDiskCache::PendingCall::~PendingCall() {}
302 int AppCacheDiskCache::Init(
303 net::CacheType cache_type
,
304 const base::FilePath
& cache_directory
,
307 const scoped_refptr
<base::SingleThreadTaskRunner
>& cache_thread
,
308 const net::CompletionCallback
& callback
) {
309 DCHECK(!is_initializing() && !disk_cache_
.get());
310 is_disabled_
= false;
311 create_backend_callback_
= new CreateBackendCallbackShim(this);
313 #if defined(APPCACHE_USE_SIMPLE_CACHE)
314 const net::BackendType backend_type
= net::CACHE_BACKEND_SIMPLE
;
316 const net::BackendType backend_type
= net::CACHE_BACKEND_DEFAULT
;
318 int rv
= disk_cache::CreateCacheBackend(
326 &(create_backend_callback_
->backend_ptr_
),
327 base::Bind(&CreateBackendCallbackShim::Callback
,
328 create_backend_callback_
));
329 if (rv
== net::ERR_IO_PENDING
)
330 init_callback_
= callback
;
332 OnCreateBackendComplete(rv
);
336 void AppCacheDiskCache::OnCreateBackendComplete(int rv
) {
338 disk_cache_
= create_backend_callback_
->backend_ptr_
.Pass();
340 create_backend_callback_
= NULL
;
342 // Invoke our clients callback function.
343 if (!init_callback_
.is_null()) {
344 init_callback_
.Run(rv
);
345 init_callback_
.Reset();
348 // Service pending calls that were queued up while we were initializing.
349 for (PendingCalls::const_iterator iter
= pending_calls_
.begin();
350 iter
< pending_calls_
.end(); ++iter
) {
351 int rv
= net::ERR_FAILED
;
352 switch (iter
->call_type
) {
354 rv
= CreateEntry(iter
->key
, iter
->entry
, iter
->callback
);
357 rv
= OpenEntry(iter
->key
, iter
->entry
, iter
->callback
);
360 rv
= DoomEntry(iter
->key
, iter
->callback
);
366 if (rv
!= net::ERR_IO_PENDING
)
367 iter
->callback
.Run(rv
);
369 pending_calls_
.clear();
372 } // namespace content