Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / appcache / appcache_disk_cache.cc
blob8419e11c5104714d8aaf27cba225f197fafa21c9
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"
7 #include "base/bind.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"
18 namespace content {
20 // A callback shim that provides storage for the 'backend_ptr' value
21 // and will delete a resulting ptr if completion occurs after its
22 // been canceled.
23 class AppCacheDiskCache::CreateBackendCallbackShim
24 : public base::RefCounted<CreateBackendCallbackShim> {
25 public:
26 explicit CreateBackendCallbackShim(AppCacheDiskCache* object)
27 : appcache_diskcache_(object) {
30 void Cancel() {
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.
41 private:
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 {
53 public:
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);
58 DCHECK(owner);
59 owner_->AddOpenEntry(this);
62 // Entry implementation.
63 int Read(int index,
64 int64 offset,
65 net::IOBuffer* buf,
66 int buf_len,
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);
76 int Write(int index,
77 int64 offset,
78 net::IOBuffer* buf,
79 int buf_len,
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();
97 delete this;
100 void Abandon() {
101 owner_ = NULL;
102 disk_cache_entry_->Close();
103 disk_cache_entry_ = NULL;
106 private:
107 ~EntryImpl() override {
108 if (owner_)
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> {
120 public:
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);
153 private:
154 friend class base::RefCounted<AppCacheDiskCache::ActiveCall>;
156 ActiveCall(const base::WeakPtr<AppCacheDiskCache>& owner,
157 Entry** entry,
158 const net::CompletionCallback& callback)
159 : owner_(owner),
160 entry_(entry),
161 callback_(callback),
162 entry_ptr_(nullptr) {
163 DCHECK(owner_);
166 ~ActiveCall() {}
168 int HandleImmediateReturnValue(int rv) {
169 if (rv == net::ERR_IO_PENDING) {
170 // OnAsyncCompletion will be called later.
171 return rv;
174 if (rv == net::OK && entry_) {
175 DCHECK(entry_ptr_);
176 *entry_ = new EntryImpl(entry_ptr_, owner_.get());
178 return rv;
181 void OnAsyncCompletion(int rv) {
182 if (rv == net::OK && entry_) {
183 DCHECK(entry_ptr_);
184 if (owner_) {
185 *entry_ = new EntryImpl(entry_ptr_, owner_.get());
186 } else {
187 entry_ptr_->Close();
188 rv = net::ERR_ABORTED;
191 callback_.Run(rv);
194 base::WeakPtr<AppCacheDiskCache> owner_;
195 Entry** entry_;
196 net::CompletionCallback callback_;
197 disk_cache::Entry* entry_ptr_;
200 AppCacheDiskCache::AppCacheDiskCache()
201 #if defined(APPCACHE_USE_SIMPLE_CACHE)
202 : AppCacheDiskCache(true)
203 #else
204 : AppCacheDiskCache(false)
205 #endif
209 AppCacheDiskCache::~AppCacheDiskCache() {
210 Disable();
213 int AppCacheDiskCache::InitWithDiskBackend(
214 const base::FilePath& disk_cache_directory,
215 int disk_cache_size,
216 bool force,
217 const scoped_refptr<base::SingleThreadTaskRunner>& cache_thread,
218 const net::CompletionCallback& callback) {
219 return Init(net::APP_CACHE,
220 disk_cache_directory,
221 disk_cache_size,
222 force,
223 cache_thread,
224 callback);
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,
230 callback);
233 void AppCacheDiskCache::Disable() {
234 if (is_disabled_)
235 return;
237 is_disabled_ = true;
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) {
250 (*iter)->Abandon();
252 open_entries_.clear();
253 disk_cache_.reset();
256 int AppCacheDiskCache::CreateEntry(int64 key, Entry** entry,
257 const net::CompletionCallback& callback) {
258 DCHECK(entry);
259 DCHECK(!callback.is_null());
260 if (is_disabled_)
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;
268 if (!disk_cache_)
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) {
277 DCHECK(entry);
278 DCHECK(!callback.is_null());
279 if (is_disabled_)
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;
287 if (!disk_cache_)
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());
297 if (is_disabled_)
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;
305 if (!disk_cache_)
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),
313 is_disabled_(false),
314 is_waiting_to_initialize_(false),
315 weak_factory_(this) {
318 AppCacheDiskCache::PendingCall::PendingCall()
319 : call_type(CREATE),
320 key(0),
321 entry(NULL) {
324 AppCacheDiskCache::PendingCall::PendingCall(PendingCallType call_type,
325 int64 key,
326 Entry** entry,
327 const net::CompletionCallback& callback)
328 : call_type(call_type),
329 key(key),
330 entry(entry),
331 callback(callback) {
334 AppCacheDiskCache::PendingCall::~PendingCall() {}
336 int AppCacheDiskCache::Init(
337 net::CacheType cache_type,
338 const base::FilePath& cache_directory,
339 int cache_size,
340 bool force,
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(
348 cache_type,
349 use_simple_cache_ ? net::CACHE_BACKEND_SIMPLE
350 : net::CACHE_BACKEND_DEFAULT,
351 cache_directory,
352 cache_size,
353 force,
354 cache_thread,
355 NULL,
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;
361 else
362 OnCreateBackendComplete(rv);
363 return rv;
366 void AppCacheDiskCache::OnCreateBackendComplete(int rv) {
367 if (rv == net::OK) {
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) {
383 case CREATE:
384 rv = CreateEntry(iter->key, iter->entry, iter->callback);
385 break;
386 case OPEN:
387 rv = OpenEntry(iter->key, iter->entry, iter->callback);
388 break;
389 case DOOM:
390 rv = DoomEntry(iter->key, iter->callback);
391 break;
392 default:
393 NOTREACHED();
394 break;
396 if (rv != net::ERR_IO_PENDING)
397 iter->callback.Run(rv);
399 pending_calls_.clear();
402 } // namespace content