Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / webkit / browser / appcache / appcache_disk_cache.cc
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.
4
5 #include "webkit/browser/appcache/appcache_disk_cache.h"
6
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/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"
15
16 namespace appcache {
17
18 // A callback shim that provides storage for the 'backend_ptr' value
19 // and will delete a resulting ptr if completion occurs after its
20 // been canceled.
21 class AppCacheDiskCache::CreateBackendCallbackShim
22     : public base::RefCounted<CreateBackendCallbackShim> {
23  public:
24   explicit CreateBackendCallbackShim(AppCacheDiskCache* object)
25       : appcache_diskcache_(object) {
26   }
27
28   void Cancel() {
29     appcache_diskcache_ = NULL;
30   }
31
32   void Callback(int rv) {
33     if (appcache_diskcache_)
34       appcache_diskcache_->OnCreateBackendComplete(rv);
35   }
36
37   scoped_ptr<disk_cache::Backend> backend_ptr_;  // Accessed directly.
38
39  private:
40   friend class base::RefCounted<CreateBackendCallbackShim>;
41
42   ~CreateBackendCallbackShim() {
43   }
44
45   AppCacheDiskCache* appcache_diskcache_;  // Unowned pointer.
46 };
47
48 // An implementation of AppCacheDiskCacheInterface::Entry that's a thin
49 // wrapper around disk_cache::Entry.
50 class AppCacheDiskCache::EntryImpl : public Entry {
51  public:
52   EntryImpl(disk_cache::Entry* disk_cache_entry,
53             AppCacheDiskCache* owner)
54       : disk_cache_entry_(disk_cache_entry), owner_(owner) {
55     DCHECK(disk_cache_entry);
56     DCHECK(owner);
57     owner_->AddOpenEntry(this);
58   }
59
60   // Entry implementation.
61   virtual int Read(int index, int64 offset, net::IOBuffer* buf, int buf_len,
62                    const net::CompletionCallback& callback) OVERRIDE {
63     if (offset < 0 || offset > kint32max)
64       return net::ERR_INVALID_ARGUMENT;
65     if (!disk_cache_entry_)
66       return net::ERR_ABORTED;
67     return disk_cache_entry_->ReadData(
68         index, static_cast<int>(offset), buf, buf_len, callback);
69   }
70
71   virtual int Write(int index, int64 offset, net::IOBuffer* buf, int buf_len,
72                     const net::CompletionCallback& callback) OVERRIDE {
73     if (offset < 0 || offset > kint32max)
74       return net::ERR_INVALID_ARGUMENT;
75     if (!disk_cache_entry_)
76       return net::ERR_ABORTED;
77     const bool kTruncate = true;
78     return disk_cache_entry_->WriteData(
79         index, static_cast<int>(offset), buf, buf_len, callback, kTruncate);
80   }
81
82   virtual int64 GetSize(int index) OVERRIDE {
83     return disk_cache_entry_ ? disk_cache_entry_->GetDataSize(index) : 0L;
84   }
85
86   virtual void Close() OVERRIDE {
87     if (disk_cache_entry_)
88       disk_cache_entry_->Close();
89     delete this;
90   }
91
92   void Abandon() {
93     owner_ = NULL;
94     disk_cache_entry_->Close();
95     disk_cache_entry_ = NULL;
96   }
97
98  private:
99   virtual ~EntryImpl() {
100     if (owner_)
101       owner_->RemoveOpenEntry(this);
102   }
103
104   disk_cache::Entry* disk_cache_entry_;
105   AppCacheDiskCache* owner_;
106 };
107
108 // Separate object to hold state for each Create, Delete, or Doom call
109 // while the call is in-flight and to produce an EntryImpl upon completion.
110 class AppCacheDiskCache::ActiveCall {
111  public:
112   explicit ActiveCall(AppCacheDiskCache* owner)
113       : entry_(NULL),
114         owner_(owner),
115         entry_ptr_(NULL) {
116   }
117
118   int CreateEntry(int64 key, Entry** entry,
119                   const net::CompletionCallback& callback) {
120     int rv = owner_->disk_cache()->CreateEntry(
121         base::Int64ToString(key), &entry_ptr_,
122         base::Bind(&ActiveCall::OnAsyncCompletion, base::Unretained(this)));
123     return HandleImmediateReturnValue(rv, entry, callback);
124   }
125
126   int OpenEntry(int64 key, Entry** entry,
127                 const net::CompletionCallback& callback) {
128     int rv = owner_->disk_cache()->OpenEntry(
129         base::Int64ToString(key), &entry_ptr_,
130         base::Bind(&ActiveCall::OnAsyncCompletion, base::Unretained(this)));
131     return HandleImmediateReturnValue(rv, entry, callback);
132   }
133
134   int DoomEntry(int64 key, const net::CompletionCallback& callback) {
135     int rv = owner_->disk_cache()->DoomEntry(
136         base::Int64ToString(key),
137         base::Bind(&ActiveCall::OnAsyncCompletion, base::Unretained(this)));
138     return HandleImmediateReturnValue(rv, NULL, callback);
139   }
140
141  private:
142   int HandleImmediateReturnValue(int rv, Entry** entry,
143                                  const net::CompletionCallback& callback) {
144     if (rv == net::ERR_IO_PENDING) {
145       // OnAsyncCompletion will be called later.
146       callback_ = callback;
147       entry_ = entry;
148       owner_->AddActiveCall(this);
149       return net::ERR_IO_PENDING;
150     }
151     if (rv == net::OK && entry)
152       *entry = new EntryImpl(entry_ptr_, owner_);
153     delete this;
154     return rv;
155   }
156
157   void OnAsyncCompletion(int rv) {
158     owner_->RemoveActiveCall(this);
159     if (rv == net::OK && entry_)
160       *entry_ = new EntryImpl(entry_ptr_, owner_);
161     callback_.Run(rv);
162     callback_.Reset();
163     delete this;
164   }
165
166   Entry** entry_;
167   net::CompletionCallback callback_;
168   AppCacheDiskCache* owner_;
169   disk_cache::Entry* entry_ptr_;
170 };
171
172 AppCacheDiskCache::AppCacheDiskCache()
173     : is_disabled_(false) {
174 }
175
176 AppCacheDiskCache::~AppCacheDiskCache() {
177   Disable();
178 }
179
180 int AppCacheDiskCache::InitWithDiskBackend(
181     const base::FilePath& disk_cache_directory, int disk_cache_size, bool force,
182     base::MessageLoopProxy* cache_thread,
183     const net::CompletionCallback& callback) {
184   return Init(net::APP_CACHE, disk_cache_directory,
185               disk_cache_size, force, cache_thread, callback);
186 }
187
188 int AppCacheDiskCache::InitWithMemBackend(
189     int mem_cache_size, const net::CompletionCallback& callback) {
190   return Init(net::MEMORY_CACHE, base::FilePath(), mem_cache_size, false, NULL,
191               callback);
192 }
193
194 void AppCacheDiskCache::Disable() {
195   if (is_disabled_)
196     return;
197
198   is_disabled_ = true;
199
200   if (create_backend_callback_.get()) {
201     create_backend_callback_->Cancel();
202     create_backend_callback_ = NULL;
203     OnCreateBackendComplete(net::ERR_ABORTED);
204   }
205
206   // We need to close open file handles in order to reinitalize the
207   // appcache system on the fly. File handles held in both entries and in
208   // the main disk_cache::Backend class need to be released.
209   for (OpenEntries::const_iterator iter = open_entries_.begin();
210        iter != open_entries_.end(); ++iter) {
211     (*iter)->Abandon();
212   }
213   open_entries_.clear();
214   disk_cache_.reset();
215   STLDeleteElements(&active_calls_);
216 }
217
218 int AppCacheDiskCache::CreateEntry(int64 key, Entry** entry,
219                                    const net::CompletionCallback& callback) {
220   DCHECK(entry);
221   DCHECK(!callback.is_null());
222   if (is_disabled_)
223     return net::ERR_ABORTED;
224
225   if (is_initializing()) {
226     pending_calls_.push_back(PendingCall(CREATE, key, entry, callback));
227     return net::ERR_IO_PENDING;
228   }
229
230   if (!disk_cache_)
231     return net::ERR_FAILED;
232
233   return (new ActiveCall(this))->CreateEntry(key, entry, callback);
234 }
235
236 int AppCacheDiskCache::OpenEntry(int64 key, Entry** entry,
237                                  const net::CompletionCallback& callback) {
238   DCHECK(entry);
239   DCHECK(!callback.is_null());
240   if (is_disabled_)
241     return net::ERR_ABORTED;
242
243   if (is_initializing()) {
244     pending_calls_.push_back(PendingCall(OPEN, key, entry, callback));
245     return net::ERR_IO_PENDING;
246   }
247
248   if (!disk_cache_)
249     return net::ERR_FAILED;
250
251   return (new ActiveCall(this))->OpenEntry(key, entry, callback);
252 }
253
254 int AppCacheDiskCache::DoomEntry(int64 key,
255                                  const net::CompletionCallback& callback) {
256   DCHECK(!callback.is_null());
257   if (is_disabled_)
258     return net::ERR_ABORTED;
259
260   if (is_initializing()) {
261     pending_calls_.push_back(PendingCall(DOOM, key, NULL, callback));
262     return net::ERR_IO_PENDING;
263   }
264
265   if (!disk_cache_)
266     return net::ERR_FAILED;
267
268   return (new ActiveCall(this))->DoomEntry(key, callback);
269 }
270
271 AppCacheDiskCache::PendingCall::PendingCall()
272     : call_type(CREATE),
273       key(0),
274       entry(NULL) {
275 }
276
277 AppCacheDiskCache::PendingCall::PendingCall(PendingCallType call_type,
278     int64 key,
279     Entry** entry,
280     const net::CompletionCallback& callback)
281     : call_type(call_type),
282       key(key),
283       entry(entry),
284       callback(callback) {
285 }
286
287 AppCacheDiskCache::PendingCall::~PendingCall() {}
288
289 int AppCacheDiskCache::Init(net::CacheType cache_type,
290                             const base::FilePath& cache_directory,
291                             int cache_size, bool force,
292                             base::MessageLoopProxy* cache_thread,
293                             const net::CompletionCallback& callback) {
294   DCHECK(!is_initializing() && !disk_cache_.get());
295   is_disabled_ = false;
296   create_backend_callback_ = new CreateBackendCallbackShim(this);
297
298 #if defined(APPCACHE_USE_SIMPLE_CACHE)
299   const net::BackendType backend_type = net::CACHE_BACKEND_SIMPLE;
300 #else
301   const net::BackendType backend_type = net::CACHE_BACKEND_DEFAULT;
302 #endif
303   int rv = disk_cache::CreateCacheBackend(
304       cache_type, backend_type, cache_directory, cache_size,
305       force, cache_thread, NULL, &(create_backend_callback_->backend_ptr_),
306       base::Bind(&CreateBackendCallbackShim::Callback,
307                  create_backend_callback_));
308   if (rv == net::ERR_IO_PENDING)
309     init_callback_ = callback;
310   else
311     OnCreateBackendComplete(rv);
312   return rv;
313 }
314
315 void AppCacheDiskCache::OnCreateBackendComplete(int rv) {
316   if (rv == net::OK) {
317     disk_cache_ = create_backend_callback_->backend_ptr_.Pass();
318   }
319   create_backend_callback_ = NULL;
320
321   // Invoke our clients callback function.
322   if (!init_callback_.is_null()) {
323     init_callback_.Run(rv);
324     init_callback_.Reset();
325   }
326
327   // Service pending calls that were queued up while we were initializing.
328   for (PendingCalls::const_iterator iter = pending_calls_.begin();
329        iter < pending_calls_.end(); ++iter) {
330     int rv = net::ERR_FAILED;
331     switch (iter->call_type) {
332       case CREATE:
333         rv = CreateEntry(iter->key, iter->entry, iter->callback);
334         break;
335       case OPEN:
336         rv = OpenEntry(iter->key, iter->entry, iter->callback);
337         break;
338       case DOOM:
339         rv = DoomEntry(iter->key, iter->callback);
340         break;
341       default:
342         NOTREACHED();
343         break;
344     }
345     if (rv != net::ERR_IO_PENDING)
346       iter->callback.Run(rv);
347   }
348   pending_calls_.clear();
349 }
350
351 }  // namespace appcache