OLD | NEW |
| (Empty) |
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 | |
OLD | NEW |