Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / content / 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 "content/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/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"
16
17 namespace content {
18
19 // A callback shim that provides storage for the 'backend_ptr' value
20 // and will delete a resulting ptr if completion occurs after its
21 // been canceled.
22 class AppCacheDiskCache::CreateBackendCallbackShim
23     : public base::RefCounted<CreateBackendCallbackShim> {
24  public:
25   explicit CreateBackendCallbackShim(AppCacheDiskCache* object)
26       : appcache_diskcache_(object) {
27   }
28
29   void Cancel() {
30     appcache_diskcache_ = NULL;
31   }
32
33   void Callback(int rv) {
34     if (appcache_diskcache_)
35       appcache_diskcache_->OnCreateBackendComplete(rv);
36   }
37
38   scoped_ptr<disk_cache::Backend> backend_ptr_;  // Accessed directly.
39
40  private:
41   friend class base::RefCounted<CreateBackendCallbackShim>;
42
43   ~CreateBackendCallbackShim() {
44   }
45
46   AppCacheDiskCache* appcache_diskcache_;  // Unowned pointer.
47 };
48
49 // An implementation of AppCacheDiskCacheInterface::Entry that's a thin
50 // wrapper around disk_cache::Entry.
51 class AppCacheDiskCache::EntryImpl : public Entry {
52  public:
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);
57     DCHECK(owner);
58     owner_->AddOpenEntry(this);
59   }
60
61   // Entry implementation.
62   int Read(int index,
63            int64 offset,
64            net::IOBuffer* buf,
65            int buf_len,
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);
73   }
74
75   int Write(int index,
76             int64 offset,
77             net::IOBuffer* buf,
78             int buf_len,
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);
87   }
88
89   int64 GetSize(int index) override {
90     return disk_cache_entry_ ? disk_cache_entry_->GetDataSize(index) : 0L;
91   }
92
93   void Close() override {
94     if (disk_cache_entry_)
95       disk_cache_entry_->Close();
96     delete this;
97   }
98
99   void Abandon() {
100     owner_ = NULL;
101     disk_cache_entry_->Close();
102     disk_cache_entry_ = NULL;
103   }
104
105  private:
106   ~EntryImpl() override {
107     if (owner_)
108       owner_->RemoveOpenEntry(this);
109   }
110
111   disk_cache::Entry* disk_cache_entry_;
112   AppCacheDiskCache* owner_;
113 };
114
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 {
118  public:
119   explicit ActiveCall(AppCacheDiskCache* owner)
120       : entry_(NULL),
121         owner_(owner),
122         entry_ptr_(NULL) {
123   }
124
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);
131   }
132
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);
139   }
140
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);
146   }
147
148  private:
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;
154       entry_ = entry;
155       owner_->AddActiveCall(this);
156       return net::ERR_IO_PENDING;
157     }
158     if (rv == net::OK && entry)
159       *entry = new EntryImpl(entry_ptr_, owner_);
160     delete this;
161     return rv;
162   }
163
164   void OnAsyncCompletion(int rv) {
165     owner_->RemoveActiveCall(this);
166     if (rv == net::OK && entry_)
167       *entry_ = new EntryImpl(entry_ptr_, owner_);
168     callback_.Run(rv);
169     callback_.Reset();
170     delete this;
171   }
172
173   Entry** entry_;
174   net::CompletionCallback callback_;
175   AppCacheDiskCache* owner_;
176   disk_cache::Entry* entry_ptr_;
177 };
178
179 AppCacheDiskCache::AppCacheDiskCache()
180     : is_disabled_(false) {
181 }
182
183 AppCacheDiskCache::~AppCacheDiskCache() {
184   Disable();
185 }
186
187 int AppCacheDiskCache::InitWithDiskBackend(
188     const base::FilePath& disk_cache_directory,
189     int disk_cache_size,
190     bool force,
191     const scoped_refptr<base::SingleThreadTaskRunner>& cache_thread,
192     const net::CompletionCallback& callback) {
193   return Init(net::APP_CACHE,
194               disk_cache_directory,
195               disk_cache_size,
196               force,
197               cache_thread,
198               callback);
199 }
200
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,
204               callback);
205 }
206
207 void AppCacheDiskCache::Disable() {
208   if (is_disabled_)
209     return;
210
211   is_disabled_ = true;
212
213   if (create_backend_callback_.get()) {
214     create_backend_callback_->Cancel();
215     create_backend_callback_ = NULL;
216     OnCreateBackendComplete(net::ERR_ABORTED);
217   }
218
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) {
224     (*iter)->Abandon();
225   }
226   open_entries_.clear();
227   disk_cache_.reset();
228   STLDeleteElements(&active_calls_);
229 }
230
231 int AppCacheDiskCache::CreateEntry(int64 key, Entry** entry,
232                                    const net::CompletionCallback& callback) {
233   DCHECK(entry);
234   DCHECK(!callback.is_null());
235   if (is_disabled_)
236     return net::ERR_ABORTED;
237
238   if (is_initializing()) {
239     pending_calls_.push_back(PendingCall(CREATE, key, entry, callback));
240     return net::ERR_IO_PENDING;
241   }
242
243   if (!disk_cache_)
244     return net::ERR_FAILED;
245
246   return (new ActiveCall(this))->CreateEntry(key, entry, callback);
247 }
248
249 int AppCacheDiskCache::OpenEntry(int64 key, Entry** entry,
250                                  const net::CompletionCallback& callback) {
251   DCHECK(entry);
252   DCHECK(!callback.is_null());
253   if (is_disabled_)
254     return net::ERR_ABORTED;
255
256   if (is_initializing()) {
257     pending_calls_.push_back(PendingCall(OPEN, key, entry, callback));
258     return net::ERR_IO_PENDING;
259   }
260
261   if (!disk_cache_)
262     return net::ERR_FAILED;
263
264   return (new ActiveCall(this))->OpenEntry(key, entry, callback);
265 }
266
267 int AppCacheDiskCache::DoomEntry(int64 key,
268                                  const net::CompletionCallback& callback) {
269   DCHECK(!callback.is_null());
270   if (is_disabled_)
271     return net::ERR_ABORTED;
272
273   if (is_initializing()) {
274     pending_calls_.push_back(PendingCall(DOOM, key, NULL, callback));
275     return net::ERR_IO_PENDING;
276   }
277
278   if (!disk_cache_)
279     return net::ERR_FAILED;
280
281   return (new ActiveCall(this))->DoomEntry(key, callback);
282 }
283
284 AppCacheDiskCache::PendingCall::PendingCall()
285     : call_type(CREATE),
286       key(0),
287       entry(NULL) {
288 }
289
290 AppCacheDiskCache::PendingCall::PendingCall(PendingCallType call_type,
291     int64 key,
292     Entry** entry,
293     const net::CompletionCallback& callback)
294     : call_type(call_type),
295       key(key),
296       entry(entry),
297       callback(callback) {
298 }
299
300 AppCacheDiskCache::PendingCall::~PendingCall() {}
301
302 int AppCacheDiskCache::Init(
303     net::CacheType cache_type,
304     const base::FilePath& cache_directory,
305     int cache_size,
306     bool force,
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);
312
313 #if defined(APPCACHE_USE_SIMPLE_CACHE)
314   const net::BackendType backend_type = net::CACHE_BACKEND_SIMPLE;
315 #else
316   const net::BackendType backend_type = net::CACHE_BACKEND_DEFAULT;
317 #endif
318   int rv = disk_cache::CreateCacheBackend(
319       cache_type,
320       backend_type,
321       cache_directory,
322       cache_size,
323       force,
324       cache_thread,
325       NULL,
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;
331   else
332     OnCreateBackendComplete(rv);
333   return rv;
334 }
335
336 void AppCacheDiskCache::OnCreateBackendComplete(int rv) {
337   if (rv == net::OK) {
338     disk_cache_ = create_backend_callback_->backend_ptr_.Pass();
339   }
340   create_backend_callback_ = NULL;
341
342   // Invoke our clients callback function.
343   if (!init_callback_.is_null()) {
344     init_callback_.Run(rv);
345     init_callback_.Reset();
346   }
347
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) {
353       case CREATE:
354         rv = CreateEntry(iter->key, iter->entry, iter->callback);
355         break;
356       case OPEN:
357         rv = OpenEntry(iter->key, iter->entry, iter->callback);
358         break;
359       case DOOM:
360         rv = DoomEntry(iter->key, iter->callback);
361         break;
362       default:
363         NOTREACHED();
364         break;
365     }
366     if (rv != net::ERR_IO_PENDING)
367       iter->callback.Run(rv);
368   }
369   pending_calls_.clear();
370 }
371
372 }  // namespace content