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/memory/ref_counted.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "net/base/cache_type.h"
16 #include "net/base/net_errors.h"
20 // A callback shim that provides storage for the 'backend_ptr' value
21 // and will delete a resulting ptr if completion occurs after its
23 class AppCacheDiskCache::CreateBackendCallbackShim
24 : public base::RefCounted
<CreateBackendCallbackShim
> {
26 explicit CreateBackendCallbackShim(AppCacheDiskCache
* object
)
27 : appcache_diskcache_(object
) {
31 appcache_diskcache_
= NULL
;
34 void Callback(int rv
) {
35 if (appcache_diskcache_
)
36 appcache_diskcache_
->OnCreateBackendComplete(rv
);
39 scoped_ptr
<disk_cache::Backend
> backend_ptr_
; // Accessed directly.
42 friend class base::RefCounted
<CreateBackendCallbackShim
>;
44 ~CreateBackendCallbackShim() {
47 AppCacheDiskCache
* appcache_diskcache_
; // Unowned pointer.
50 // An implementation of AppCacheDiskCacheInterface::Entry that's a thin
51 // wrapper around disk_cache::Entry.
52 class AppCacheDiskCache::EntryImpl
: public Entry
{
54 EntryImpl(disk_cache::Entry
* disk_cache_entry
,
55 AppCacheDiskCache
* owner
)
56 : disk_cache_entry_(disk_cache_entry
), owner_(owner
) {
57 DCHECK(disk_cache_entry
);
59 owner_
->AddOpenEntry(this);
62 // Entry implementation.
67 const net::CompletionCallback
& callback
) override
{
68 if (offset
< 0 || offset
> kint32max
)
69 return net::ERR_INVALID_ARGUMENT
;
70 if (!disk_cache_entry_
)
71 return net::ERR_ABORTED
;
72 return disk_cache_entry_
->ReadData(
73 index
, static_cast<int>(offset
), buf
, buf_len
, callback
);
80 const net::CompletionCallback
& callback
) override
{
81 if (offset
< 0 || offset
> kint32max
)
82 return net::ERR_INVALID_ARGUMENT
;
83 if (!disk_cache_entry_
)
84 return net::ERR_ABORTED
;
85 const bool kTruncate
= true;
86 return disk_cache_entry_
->WriteData(
87 index
, static_cast<int>(offset
), buf
, buf_len
, callback
, kTruncate
);
90 int64
GetSize(int index
) override
{
91 return disk_cache_entry_
? disk_cache_entry_
->GetDataSize(index
) : 0L;
94 void Close() override
{
95 if (disk_cache_entry_
)
96 disk_cache_entry_
->Close();
102 disk_cache_entry_
->Close();
103 disk_cache_entry_
= NULL
;
107 ~EntryImpl() override
{
109 owner_
->RemoveOpenEntry(this);
112 disk_cache::Entry
* disk_cache_entry_
;
113 AppCacheDiskCache
* owner_
;
116 // Separate object to hold state for each Create, Delete, or Doom call
117 // while the call is in-flight and to produce an EntryImpl upon completion.
118 class AppCacheDiskCache::ActiveCall
119 : public base::RefCounted
<AppCacheDiskCache::ActiveCall
> {
121 static int CreateEntry(const base::WeakPtr
<AppCacheDiskCache
>& owner
,
122 int64 key
, Entry
** entry
,
123 const net::CompletionCallback
& callback
) {
124 scoped_refptr
<ActiveCall
> active_call(
125 new ActiveCall(owner
, entry
, callback
));
126 int rv
= owner
->disk_cache()->CreateEntry(
127 base::Int64ToString(key
), &active_call
->entry_ptr_
,
128 base::Bind(&ActiveCall::OnAsyncCompletion
, active_call
));
129 return active_call
->HandleImmediateReturnValue(rv
);
132 static int OpenEntry(const base::WeakPtr
<AppCacheDiskCache
>& owner
,
133 int64 key
, Entry
** entry
,
134 const net::CompletionCallback
& callback
) {
135 scoped_refptr
<ActiveCall
> active_call(
136 new ActiveCall(owner
, entry
, callback
));
137 int rv
= owner
->disk_cache()->OpenEntry(
138 base::Int64ToString(key
), &active_call
->entry_ptr_
,
139 base::Bind(&ActiveCall::OnAsyncCompletion
, active_call
));
140 return active_call
->HandleImmediateReturnValue(rv
);
143 static int DoomEntry(const base::WeakPtr
<AppCacheDiskCache
>& owner
,
144 int64 key
, const net::CompletionCallback
& callback
) {
145 scoped_refptr
<ActiveCall
> active_call(
146 new ActiveCall(owner
, nullptr, callback
));
147 int rv
= owner
->disk_cache()->DoomEntry(
148 base::Int64ToString(key
),
149 base::Bind(&ActiveCall::OnAsyncCompletion
, active_call
));
150 return active_call
->HandleImmediateReturnValue(rv
);
154 friend class base::RefCounted
<AppCacheDiskCache::ActiveCall
>;
156 ActiveCall(const base::WeakPtr
<AppCacheDiskCache
>& owner
,
158 const net::CompletionCallback
& callback
)
162 entry_ptr_(nullptr) {
168 int HandleImmediateReturnValue(int rv
) {
169 if (rv
== net::ERR_IO_PENDING
) {
170 // OnAsyncCompletion will be called later.
174 if (rv
== net::OK
&& entry_
) {
176 *entry_
= new EntryImpl(entry_ptr_
, owner_
.get());
181 void OnAsyncCompletion(int rv
) {
182 if (rv
== net::OK
&& entry_
) {
185 *entry_
= new EntryImpl(entry_ptr_
, owner_
.get());
188 rv
= net::ERR_ABORTED
;
194 base::WeakPtr
<AppCacheDiskCache
> owner_
;
196 net::CompletionCallback callback_
;
197 disk_cache::Entry
* entry_ptr_
;
200 AppCacheDiskCache::AppCacheDiskCache()
201 #if defined(APPCACHE_USE_SIMPLE_CACHE)
202 : AppCacheDiskCache(true)
204 : AppCacheDiskCache(false)
209 AppCacheDiskCache::~AppCacheDiskCache() {
213 int AppCacheDiskCache::InitWithDiskBackend(
214 const base::FilePath
& disk_cache_directory
,
217 const scoped_refptr
<base::SingleThreadTaskRunner
>& cache_thread
,
218 const net::CompletionCallback
& callback
) {
219 return Init(net::APP_CACHE
,
220 disk_cache_directory
,
227 int AppCacheDiskCache::InitWithMemBackend(
228 int mem_cache_size
, const net::CompletionCallback
& callback
) {
229 return Init(net::MEMORY_CACHE
, base::FilePath(), mem_cache_size
, false, NULL
,
233 void AppCacheDiskCache::Disable() {
239 if (create_backend_callback_
.get()) {
240 create_backend_callback_
->Cancel();
241 create_backend_callback_
= NULL
;
242 OnCreateBackendComplete(net::ERR_ABORTED
);
245 // We need to close open file handles in order to reinitalize the
246 // appcache system on the fly. File handles held in both entries and in
247 // the main disk_cache::Backend class need to be released.
248 for (OpenEntries::const_iterator iter
= open_entries_
.begin();
249 iter
!= open_entries_
.end(); ++iter
) {
252 open_entries_
.clear();
256 int AppCacheDiskCache::CreateEntry(int64 key
, Entry
** entry
,
257 const net::CompletionCallback
& callback
) {
259 DCHECK(!callback
.is_null());
261 return net::ERR_ABORTED
;
263 if (is_initializing_or_waiting_to_initialize()) {
264 pending_calls_
.push_back(PendingCall(CREATE
, key
, entry
, callback
));
265 return net::ERR_IO_PENDING
;
269 return net::ERR_FAILED
;
271 return ActiveCall::CreateEntry(
272 weak_factory_
.GetWeakPtr(), key
, entry
, callback
);
275 int AppCacheDiskCache::OpenEntry(int64 key
, Entry
** entry
,
276 const net::CompletionCallback
& callback
) {
278 DCHECK(!callback
.is_null());
280 return net::ERR_ABORTED
;
282 if (is_initializing_or_waiting_to_initialize()) {
283 pending_calls_
.push_back(PendingCall(OPEN
, key
, entry
, callback
));
284 return net::ERR_IO_PENDING
;
288 return net::ERR_FAILED
;
290 return ActiveCall::OpenEntry(
291 weak_factory_
.GetWeakPtr(), key
, entry
, callback
);
294 int AppCacheDiskCache::DoomEntry(int64 key
,
295 const net::CompletionCallback
& callback
) {
296 DCHECK(!callback
.is_null());
298 return net::ERR_ABORTED
;
300 if (is_initializing_or_waiting_to_initialize()) {
301 pending_calls_
.push_back(PendingCall(DOOM
, key
, NULL
, callback
));
302 return net::ERR_IO_PENDING
;
306 return net::ERR_FAILED
;
308 return ActiveCall::DoomEntry(weak_factory_
.GetWeakPtr(), key
, callback
);
311 AppCacheDiskCache::AppCacheDiskCache(bool use_simple_cache
)
312 : use_simple_cache_(use_simple_cache
),
314 is_waiting_to_initialize_(false),
315 weak_factory_(this) {
318 AppCacheDiskCache::PendingCall::PendingCall()
324 AppCacheDiskCache::PendingCall::PendingCall(PendingCallType call_type
,
327 const net::CompletionCallback
& callback
)
328 : call_type(call_type
),
334 AppCacheDiskCache::PendingCall::~PendingCall() {}
336 int AppCacheDiskCache::Init(
337 net::CacheType cache_type
,
338 const base::FilePath
& cache_directory
,
341 const scoped_refptr
<base::SingleThreadTaskRunner
>& cache_thread
,
342 const net::CompletionCallback
& callback
) {
343 DCHECK(!is_initializing_or_waiting_to_initialize() && !disk_cache_
.get());
344 is_disabled_
= false;
345 create_backend_callback_
= new CreateBackendCallbackShim(this);
347 int rv
= disk_cache::CreateCacheBackend(
349 use_simple_cache_
? net::CACHE_BACKEND_SIMPLE
350 : net::CACHE_BACKEND_DEFAULT
,
356 &(create_backend_callback_
->backend_ptr_
),
357 base::Bind(&CreateBackendCallbackShim::Callback
,
358 create_backend_callback_
));
359 if (rv
== net::ERR_IO_PENDING
)
360 init_callback_
= callback
;
362 OnCreateBackendComplete(rv
);
366 void AppCacheDiskCache::OnCreateBackendComplete(int rv
) {
368 disk_cache_
= create_backend_callback_
->backend_ptr_
.Pass();
370 create_backend_callback_
= NULL
;
372 // Invoke our clients callback function.
373 if (!init_callback_
.is_null()) {
374 init_callback_
.Run(rv
);
375 init_callback_
.Reset();
378 // Service pending calls that were queued up while we were initializing.
379 for (PendingCalls::const_iterator iter
= pending_calls_
.begin();
380 iter
< pending_calls_
.end(); ++iter
) {
381 int rv
= net::ERR_FAILED
;
382 switch (iter
->call_type
) {
384 rv
= CreateEntry(iter
->key
, iter
->entry
, iter
->callback
);
387 rv
= OpenEntry(iter
->key
, iter
->entry
, iter
->callback
);
390 rv
= DoomEntry(iter
->key
, iter
->callback
);
396 if (rv
!= net::ERR_IO_PENDING
)
397 iter
->callback
.Run(rv
);
399 pending_calls_
.clear();
402 } // namespace content