When Retrier succeeds, record errors it encountered.
[chromium-blink-merge.git] / webkit / appcache / appcache_service.cc
blob70fed116f3ce1603be4b7bff9fa7f9e78184dc74
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 "webkit/appcache/appcache_service.h"
7 #include <functional>
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/logging.h"
12 #include "base/message_loop.h"
13 #include "base/stl_util.h"
14 #include "net/base/completion_callback.h"
15 #include "net/base/io_buffer.h"
16 #include "webkit/appcache/appcache.h"
17 #include "webkit/appcache/appcache_backend_impl.h"
18 #include "webkit/appcache/appcache_entry.h"
19 #include "webkit/appcache/appcache_histograms.h"
20 #include "webkit/appcache/appcache_policy.h"
21 #include "webkit/appcache/appcache_quota_client.h"
22 #include "webkit/appcache/appcache_response.h"
23 #include "webkit/appcache/appcache_storage_impl.h"
24 #include "webkit/quota/quota_manager.h"
25 #include "webkit/quota/special_storage_policy.h"
27 namespace appcache {
29 namespace {
31 void DeferredCallback(const net::CompletionCallback& callback, int rv) {
32 callback.Run(rv);
35 } // namespace
37 AppCacheInfoCollection::AppCacheInfoCollection() {}
39 AppCacheInfoCollection::~AppCacheInfoCollection() {}
41 // AsyncHelper -------
43 class AppCacheService::AsyncHelper
44 : public AppCacheStorage::Delegate {
45 public:
46 AsyncHelper(AppCacheService* service,
47 const net::CompletionCallback& callback)
48 : service_(service), callback_(callback) {
49 service_->pending_helpers_.insert(this);
52 virtual ~AsyncHelper() {
53 if (service_)
54 service_->pending_helpers_.erase(this);
57 virtual void Start() = 0;
58 virtual void Cancel();
60 protected:
61 void CallCallback(int rv) {
62 if (!callback_.is_null()) {
63 // Defer to guarantee async completion.
64 base::MessageLoop::current()->PostTask(
65 FROM_HERE, base::Bind(&DeferredCallback, callback_, rv));
67 callback_.Reset();
70 AppCacheService* service_;
71 net::CompletionCallback callback_;
74 void AppCacheService::AsyncHelper::Cancel() {
75 if (!callback_.is_null()) {
76 callback_.Run(net::ERR_ABORTED);
77 callback_.Reset();
79 service_->storage()->CancelDelegateCallbacks(this);
80 service_ = NULL;
83 // CanHandleOfflineHelper -------
85 class AppCacheService::CanHandleOfflineHelper : AsyncHelper {
86 public:
87 CanHandleOfflineHelper(
88 AppCacheService* service, const GURL& url,
89 const GURL& first_party, const net::CompletionCallback& callback)
90 : AsyncHelper(service, callback),
91 url_(url),
92 first_party_(first_party) {
95 virtual void Start() OVERRIDE {
96 AppCachePolicy* policy = service_->appcache_policy();
97 if (policy && !policy->CanLoadAppCache(url_, first_party_)) {
98 CallCallback(net::ERR_FAILED);
99 delete this;
100 return;
103 service_->storage()->FindResponseForMainRequest(url_, GURL(), this);
106 private:
107 // AppCacheStorage::Delegate implementation.
108 virtual void OnMainResponseFound(
109 const GURL& url, const AppCacheEntry& entry,
110 const GURL& fallback_url, const AppCacheEntry& fallback_entry,
111 int64 cache_id, int64 group_id, const GURL& mainfest_url) OVERRIDE;
113 GURL url_;
114 GURL first_party_;
116 DISALLOW_COPY_AND_ASSIGN(CanHandleOfflineHelper);
119 void AppCacheService::CanHandleOfflineHelper::OnMainResponseFound(
120 const GURL& url, const AppCacheEntry& entry,
121 const GURL& fallback_url, const AppCacheEntry& fallback_entry,
122 int64 cache_id, int64 group_id, const GURL& manifest_url) {
123 bool can = (entry.has_response_id() || fallback_entry.has_response_id());
124 CallCallback(can ? net::OK : net::ERR_FAILED);
125 delete this;
128 // DeleteHelper -------
130 class AppCacheService::DeleteHelper : public AsyncHelper {
131 public:
132 DeleteHelper(
133 AppCacheService* service, const GURL& manifest_url,
134 const net::CompletionCallback& callback)
135 : AsyncHelper(service, callback), manifest_url_(manifest_url) {
138 virtual void Start() OVERRIDE {
139 service_->storage()->LoadOrCreateGroup(manifest_url_, this);
142 private:
143 // AppCacheStorage::Delegate implementation.
144 virtual void OnGroupLoaded(
145 appcache::AppCacheGroup* group, const GURL& manifest_url) OVERRIDE;
146 virtual void OnGroupMadeObsolete(
147 appcache::AppCacheGroup* group, bool success) OVERRIDE;
149 GURL manifest_url_;
150 DISALLOW_COPY_AND_ASSIGN(DeleteHelper);
153 void AppCacheService::DeleteHelper::OnGroupLoaded(
154 appcache::AppCacheGroup* group, const GURL& manifest_url) {
155 if (group) {
156 group->set_being_deleted(true);
157 group->CancelUpdate();
158 service_->storage()->MakeGroupObsolete(group, this);
159 } else {
160 CallCallback(net::ERR_FAILED);
161 delete this;
165 void AppCacheService::DeleteHelper::OnGroupMadeObsolete(
166 appcache::AppCacheGroup* group, bool success) {
167 CallCallback(success ? net::OK : net::ERR_FAILED);
168 delete this;
171 // DeleteOriginHelper -------
173 class AppCacheService::DeleteOriginHelper : public AsyncHelper {
174 public:
175 DeleteOriginHelper(
176 AppCacheService* service, const GURL& origin,
177 const net::CompletionCallback& callback)
178 : AsyncHelper(service, callback), origin_(origin),
179 num_caches_to_delete_(0), successes_(0), failures_(0) {
182 virtual void Start() OVERRIDE {
183 // We start by listing all caches, continues in OnAllInfo().
184 service_->storage()->GetAllInfo(this);
187 private:
188 // AppCacheStorage::Delegate implementation.
189 virtual void OnAllInfo(AppCacheInfoCollection* collection) OVERRIDE;
190 virtual void OnGroupLoaded(
191 appcache::AppCacheGroup* group, const GURL& manifest_url) OVERRIDE;
192 virtual void OnGroupMadeObsolete(
193 appcache::AppCacheGroup* group, bool success) OVERRIDE;
195 void CacheCompleted(bool success);
197 GURL origin_;
198 int num_caches_to_delete_;
199 int successes_;
200 int failures_;
202 DISALLOW_COPY_AND_ASSIGN(DeleteOriginHelper);
205 void AppCacheService::DeleteOriginHelper::OnAllInfo(
206 AppCacheInfoCollection* collection) {
207 if (!collection) {
208 // Failed to get a listing.
209 CallCallback(net::ERR_FAILED);
210 delete this;
211 return;
214 std::map<GURL, AppCacheInfoVector>::iterator found =
215 collection->infos_by_origin.find(origin_);
216 if (found == collection->infos_by_origin.end() || found->second.empty()) {
217 // No caches for this origin.
218 CallCallback(net::OK);
219 delete this;
220 return;
223 // We have some caches to delete.
224 const AppCacheInfoVector& caches_to_delete = found->second;
225 successes_ = 0;
226 failures_ = 0;
227 num_caches_to_delete_ = static_cast<int>(caches_to_delete.size());
228 for (AppCacheInfoVector::const_iterator iter = caches_to_delete.begin();
229 iter != caches_to_delete.end(); ++iter) {
230 service_->storage()->LoadOrCreateGroup(iter->manifest_url, this);
234 void AppCacheService::DeleteOriginHelper::OnGroupLoaded(
235 appcache::AppCacheGroup* group, const GURL& manifest_url) {
236 if (group) {
237 group->set_being_deleted(true);
238 group->CancelUpdate();
239 service_->storage()->MakeGroupObsolete(group, this);
240 } else {
241 CacheCompleted(false);
245 void AppCacheService::DeleteOriginHelper::OnGroupMadeObsolete(
246 appcache::AppCacheGroup* group, bool success) {
247 CacheCompleted(success);
250 void AppCacheService::DeleteOriginHelper::CacheCompleted(bool success) {
251 if (success)
252 ++successes_;
253 else
254 ++failures_;
255 if ((successes_ + failures_) < num_caches_to_delete_)
256 return;
258 CallCallback(!failures_ ? net::OK : net::ERR_FAILED);
259 delete this;
263 // GetInfoHelper -------
265 class AppCacheService::GetInfoHelper : AsyncHelper {
266 public:
267 GetInfoHelper(
268 AppCacheService* service, AppCacheInfoCollection* collection,
269 const net::CompletionCallback& callback)
270 : AsyncHelper(service, callback), collection_(collection) {
273 virtual void Start() OVERRIDE {
274 service_->storage()->GetAllInfo(this);
277 private:
278 // AppCacheStorage::Delegate implementation.
279 virtual void OnAllInfo(AppCacheInfoCollection* collection) OVERRIDE;
281 scoped_refptr<AppCacheInfoCollection> collection_;
283 DISALLOW_COPY_AND_ASSIGN(GetInfoHelper);
286 void AppCacheService::GetInfoHelper::OnAllInfo(
287 AppCacheInfoCollection* collection) {
288 if (collection)
289 collection->infos_by_origin.swap(collection_->infos_by_origin);
290 CallCallback(collection ? net::OK : net::ERR_FAILED);
291 delete this;
294 // CheckResponseHelper -------
296 class AppCacheService::CheckResponseHelper : AsyncHelper {
297 public:
298 CheckResponseHelper(
299 AppCacheService* service, const GURL& manifest_url, int64 cache_id,
300 int64 response_id)
301 : AsyncHelper(service, net::CompletionCallback()),
302 manifest_url_(manifest_url),
303 cache_id_(cache_id),
304 response_id_(response_id),
305 kIOBufferSize(32 * 1024),
306 expected_total_size_(0),
307 amount_headers_read_(0),
308 amount_data_read_(0) {
311 virtual void Start() OVERRIDE {
312 service_->storage()->LoadOrCreateGroup(manifest_url_, this);
315 virtual void Cancel() OVERRIDE {
316 AppCacheHistograms::CountCheckResponseResult(
317 AppCacheHistograms::CHECK_CANCELED);
318 response_reader_.reset();
319 AsyncHelper::Cancel();
322 private:
323 virtual void OnGroupLoaded(AppCacheGroup* group,
324 const GURL& manifest_url) OVERRIDE;
325 void OnReadInfoComplete(int result);
326 void OnReadDataComplete(int result);
328 // Inputs describing what to check.
329 GURL manifest_url_;
330 int64 cache_id_;
331 int64 response_id_;
333 // Internals used to perform the checks.
334 const int kIOBufferSize;
335 scoped_refptr<AppCache> cache_;
336 scoped_ptr<AppCacheResponseReader> response_reader_;
337 scoped_refptr<HttpResponseInfoIOBuffer> info_buffer_;
338 scoped_refptr<net::IOBuffer> data_buffer_;
339 int64 expected_total_size_;
340 int amount_headers_read_;
341 int amount_data_read_;
342 DISALLOW_COPY_AND_ASSIGN(CheckResponseHelper);
345 void AppCacheService::CheckResponseHelper::OnGroupLoaded(
346 AppCacheGroup* group, const GURL& manifest_url) {
347 DCHECK_EQ(manifest_url_, manifest_url);
348 if (!group || !group->newest_complete_cache() || group->is_being_deleted() ||
349 group->is_obsolete()) {
350 AppCacheHistograms::CountCheckResponseResult(
351 AppCacheHistograms::MANIFEST_OUT_OF_DATE);
352 delete this;
353 return;
356 cache_ = group->newest_complete_cache();
357 const AppCacheEntry* entry = cache_->GetEntryWithResponseId(response_id_);
358 if (!entry) {
359 if (cache_->cache_id() == cache_id_) {
360 AppCacheHistograms::CountCheckResponseResult(
361 AppCacheHistograms::ENTRY_NOT_FOUND);
362 service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
363 } else {
364 AppCacheHistograms::CountCheckResponseResult(
365 AppCacheHistograms::RESPONSE_OUT_OF_DATE);
367 delete this;
368 return;
371 // Verify that we can read the response info and data.
372 expected_total_size_ = entry->response_size();
373 response_reader_.reset(service_->storage()->CreateResponseReader(
374 manifest_url_, group->group_id(), response_id_));
375 info_buffer_ = new HttpResponseInfoIOBuffer();
376 response_reader_->ReadInfo(
377 info_buffer_, base::Bind(&CheckResponseHelper::OnReadInfoComplete,
378 base::Unretained(this)));
381 void AppCacheService::CheckResponseHelper::OnReadInfoComplete(int result) {
382 if (result < 0) {
383 AppCacheHistograms::CountCheckResponseResult(
384 AppCacheHistograms::READ_HEADERS_ERROR);
385 service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
386 delete this;
387 return;
389 amount_headers_read_ = result;
391 // Start reading the data.
392 data_buffer_ = new net::IOBuffer(kIOBufferSize);
393 response_reader_->ReadData(
394 data_buffer_, kIOBufferSize,
395 base::Bind(&CheckResponseHelper::OnReadDataComplete,
396 base::Unretained(this)));
399 void AppCacheService::CheckResponseHelper::OnReadDataComplete(int result) {
400 if (result > 0) {
401 // Keep reading until we've read thru everything or failed to read.
402 amount_data_read_ += result;
403 response_reader_->ReadData(
404 data_buffer_, kIOBufferSize,
405 base::Bind(&CheckResponseHelper::OnReadDataComplete,
406 base::Unretained(this)));
407 return;
410 AppCacheHistograms::CheckResponseResultType check_result;
411 if (result < 0)
412 check_result = AppCacheHistograms::READ_DATA_ERROR;
413 else if (info_buffer_->response_data_size != amount_data_read_ ||
414 expected_total_size_ != amount_data_read_ + amount_headers_read_)
415 check_result = AppCacheHistograms::UNEXPECTED_DATA_SIZE;
416 else
417 check_result = AppCacheHistograms::RESPONSE_OK;
418 AppCacheHistograms::CountCheckResponseResult(check_result);
420 if (check_result != AppCacheHistograms::RESPONSE_OK)
421 service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
422 delete this;
426 // AppCacheService -------
428 AppCacheService::AppCacheService(quota::QuotaManagerProxy* quota_manager_proxy)
429 : appcache_policy_(NULL), quota_client_(NULL),
430 quota_manager_proxy_(quota_manager_proxy),
431 request_context_(NULL),
432 force_keep_session_state_(false) {
433 if (quota_manager_proxy_) {
434 quota_client_ = new AppCacheQuotaClient(this);
435 quota_manager_proxy_->RegisterClient(quota_client_);
439 AppCacheService::~AppCacheService() {
440 DCHECK(backends_.empty());
441 std::for_each(pending_helpers_.begin(),
442 pending_helpers_.end(),
443 std::mem_fun(&AsyncHelper::Cancel));
444 STLDeleteElements(&pending_helpers_);
445 if (quota_client_)
446 quota_client_->NotifyAppCacheDestroyed();
448 // Destroy storage_ first; ~AppCacheStorageImpl accesses other data members
449 // (special_storage_policy_).
450 storage_.reset();
453 void AppCacheService::Initialize(const base::FilePath& cache_directory,
454 base::MessageLoopProxy* db_thread,
455 base::MessageLoopProxy* cache_thread) {
456 DCHECK(!storage_.get());
457 AppCacheStorageImpl* storage = new AppCacheStorageImpl(this);
458 storage->Initialize(cache_directory, db_thread, cache_thread);
459 storage_.reset(storage);
462 void AppCacheService::CanHandleMainResourceOffline(
463 const GURL& url,
464 const GURL& first_party,
465 const net::CompletionCallback& callback) {
466 CanHandleOfflineHelper* helper =
467 new CanHandleOfflineHelper(this, url, first_party, callback);
468 helper->Start();
471 void AppCacheService::GetAllAppCacheInfo(
472 AppCacheInfoCollection* collection,
473 const net::CompletionCallback& callback) {
474 DCHECK(collection);
475 GetInfoHelper* helper = new GetInfoHelper(this, collection, callback);
476 helper->Start();
479 void AppCacheService::DeleteAppCacheGroup(
480 const GURL& manifest_url,
481 const net::CompletionCallback& callback) {
482 DeleteHelper* helper = new DeleteHelper(this, manifest_url, callback);
483 helper->Start();
486 void AppCacheService::DeleteAppCachesForOrigin(
487 const GURL& origin, const net::CompletionCallback& callback) {
488 DeleteOriginHelper* helper = new DeleteOriginHelper(this, origin, callback);
489 helper->Start();
492 void AppCacheService::CheckAppCacheResponse(const GURL& manifest_url,
493 int64 cache_id,
494 int64 response_id) {
495 CheckResponseHelper* helper = new CheckResponseHelper(
496 this, manifest_url, cache_id, response_id);
497 helper->Start();
500 void AppCacheService::set_special_storage_policy(
501 quota::SpecialStoragePolicy* policy) {
502 special_storage_policy_ = policy;
505 void AppCacheService::RegisterBackend(
506 AppCacheBackendImpl* backend_impl) {
507 DCHECK(backends_.find(backend_impl->process_id()) == backends_.end());
508 backends_.insert(
509 BackendMap::value_type(backend_impl->process_id(), backend_impl));
512 void AppCacheService::UnregisterBackend(
513 AppCacheBackendImpl* backend_impl) {
514 backends_.erase(backend_impl->process_id());
517 } // namespace appcache