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/browser/appcache/appcache_service.h"
10 #include "base/bind_helpers.h"
11 #include "base/logging.h"
12 #include "base/message_loop/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/browser/appcache/appcache.h"
17 #include "webkit/browser/appcache/appcache_backend_impl.h"
18 #include "webkit/browser/appcache/appcache_entry.h"
19 #include "webkit/browser/appcache/appcache_executable_handler.h"
20 #include "webkit/browser/appcache/appcache_histograms.h"
21 #include "webkit/browser/appcache/appcache_policy.h"
22 #include "webkit/browser/appcache/appcache_quota_client.h"
23 #include "webkit/browser/appcache/appcache_response.h"
24 #include "webkit/browser/appcache/appcache_storage_impl.h"
25 #include "webkit/browser/quota/special_storage_policy.h"
31 void DeferredCallback(const net::CompletionCallback& callback, int rv) {
37 AppCacheInfoCollection::AppCacheInfoCollection() {}
39 AppCacheInfoCollection::~AppCacheInfoCollection() {}
41 // AsyncHelper -------
43 class AppCacheService::AsyncHelper
44 : public AppCacheStorage::Delegate {
46 AsyncHelper(AppCacheService* service,
47 const net::CompletionCallback& callback)
48 : service_(service), callback_(callback) {
49 service_->pending_helpers_.insert(this);
52 virtual ~AsyncHelper() {
54 service_->pending_helpers_.erase(this);
57 virtual void Start() = 0;
58 virtual void Cancel();
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));
70 AppCacheService* service_;
71 net::CompletionCallback callback_;
74 void AppCacheService::AsyncHelper::Cancel() {
75 if (!callback_.is_null()) {
76 callback_.Run(net::ERR_ABORTED);
79 service_->storage()->CancelDelegateCallbacks(this);
83 // CanHandleOfflineHelper -------
85 class AppCacheService::CanHandleOfflineHelper : AsyncHelper {
87 CanHandleOfflineHelper(
88 AppCacheService* service, const GURL& url,
89 const GURL& first_party, const net::CompletionCallback& callback)
90 : AsyncHelper(service, callback),
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);
103 service_->storage()->FindResponseForMainRequest(url_, GURL(), this);
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;
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);
128 // DeleteHelper -------
130 class AppCacheService::DeleteHelper : public AsyncHelper {
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);
143 // AppCacheStorage::Delegate implementation.
144 virtual void OnGroupLoaded(
145 appcache::AppCacheGroup* group, const GURL& manifest_url) OVERRIDE;
146 virtual void OnGroupMadeObsolete(appcache::AppCacheGroup* group,
148 int response_code) OVERRIDE;
151 DISALLOW_COPY_AND_ASSIGN(DeleteHelper);
154 void AppCacheService::DeleteHelper::OnGroupLoaded(
155 appcache::AppCacheGroup* group, const GURL& manifest_url) {
157 group->set_being_deleted(true);
158 group->CancelUpdate();
159 service_->storage()->MakeGroupObsolete(group, this, 0);
161 CallCallback(net::ERR_FAILED);
166 void AppCacheService::DeleteHelper::OnGroupMadeObsolete(
167 appcache::AppCacheGroup* group,
170 CallCallback(success ? net::OK : net::ERR_FAILED);
174 // DeleteOriginHelper -------
176 class AppCacheService::DeleteOriginHelper : public AsyncHelper {
179 AppCacheService* service, const GURL& origin,
180 const net::CompletionCallback& callback)
181 : AsyncHelper(service, callback), origin_(origin),
182 num_caches_to_delete_(0), successes_(0), failures_(0) {
185 virtual void Start() OVERRIDE {
186 // We start by listing all caches, continues in OnAllInfo().
187 service_->storage()->GetAllInfo(this);
191 // AppCacheStorage::Delegate implementation.
192 virtual void OnAllInfo(AppCacheInfoCollection* collection) OVERRIDE;
193 virtual void OnGroupLoaded(
194 appcache::AppCacheGroup* group, const GURL& manifest_url) OVERRIDE;
195 virtual void OnGroupMadeObsolete(appcache::AppCacheGroup* group,
197 int response_code) OVERRIDE;
199 void CacheCompleted(bool success);
202 int num_caches_to_delete_;
206 DISALLOW_COPY_AND_ASSIGN(DeleteOriginHelper);
209 void AppCacheService::DeleteOriginHelper::OnAllInfo(
210 AppCacheInfoCollection* collection) {
212 // Failed to get a listing.
213 CallCallback(net::ERR_FAILED);
218 std::map<GURL, AppCacheInfoVector>::iterator found =
219 collection->infos_by_origin.find(origin_);
220 if (found == collection->infos_by_origin.end() || found->second.empty()) {
221 // No caches for this origin.
222 CallCallback(net::OK);
227 // We have some caches to delete.
228 const AppCacheInfoVector& caches_to_delete = found->second;
231 num_caches_to_delete_ = static_cast<int>(caches_to_delete.size());
232 for (AppCacheInfoVector::const_iterator iter = caches_to_delete.begin();
233 iter != caches_to_delete.end(); ++iter) {
234 service_->storage()->LoadOrCreateGroup(iter->manifest_url, this);
238 void AppCacheService::DeleteOriginHelper::OnGroupLoaded(
239 appcache::AppCacheGroup* group, const GURL& manifest_url) {
241 group->set_being_deleted(true);
242 group->CancelUpdate();
243 service_->storage()->MakeGroupObsolete(group, this, 0);
245 CacheCompleted(false);
249 void AppCacheService::DeleteOriginHelper::OnGroupMadeObsolete(
250 appcache::AppCacheGroup* group,
253 CacheCompleted(success);
256 void AppCacheService::DeleteOriginHelper::CacheCompleted(bool success) {
261 if ((successes_ + failures_) < num_caches_to_delete_)
264 CallCallback(!failures_ ? net::OK : net::ERR_FAILED);
269 // GetInfoHelper -------
271 class AppCacheService::GetInfoHelper : AsyncHelper {
274 AppCacheService* service, AppCacheInfoCollection* collection,
275 const net::CompletionCallback& callback)
276 : AsyncHelper(service, callback), collection_(collection) {
279 virtual void Start() OVERRIDE {
280 service_->storage()->GetAllInfo(this);
284 // AppCacheStorage::Delegate implementation.
285 virtual void OnAllInfo(AppCacheInfoCollection* collection) OVERRIDE;
287 scoped_refptr<AppCacheInfoCollection> collection_;
289 DISALLOW_COPY_AND_ASSIGN(GetInfoHelper);
292 void AppCacheService::GetInfoHelper::OnAllInfo(
293 AppCacheInfoCollection* collection) {
295 collection->infos_by_origin.swap(collection_->infos_by_origin);
296 CallCallback(collection ? net::OK : net::ERR_FAILED);
300 // CheckResponseHelper -------
302 class AppCacheService::CheckResponseHelper : AsyncHelper {
305 AppCacheService* service, const GURL& manifest_url, int64 cache_id,
307 : AsyncHelper(service, net::CompletionCallback()),
308 manifest_url_(manifest_url),
310 response_id_(response_id),
311 kIOBufferSize(32 * 1024),
312 expected_total_size_(0),
313 amount_headers_read_(0),
314 amount_data_read_(0) {
317 virtual void Start() OVERRIDE {
318 service_->storage()->LoadOrCreateGroup(manifest_url_, this);
321 virtual void Cancel() OVERRIDE {
322 AppCacheHistograms::CountCheckResponseResult(
323 AppCacheHistograms::CHECK_CANCELED);
324 response_reader_.reset();
325 AsyncHelper::Cancel();
329 virtual void OnGroupLoaded(AppCacheGroup* group,
330 const GURL& manifest_url) OVERRIDE;
331 void OnReadInfoComplete(int result);
332 void OnReadDataComplete(int result);
334 // Inputs describing what to check.
339 // Internals used to perform the checks.
340 const int kIOBufferSize;
341 scoped_refptr<AppCache> cache_;
342 scoped_ptr<AppCacheResponseReader> response_reader_;
343 scoped_refptr<HttpResponseInfoIOBuffer> info_buffer_;
344 scoped_refptr<net::IOBuffer> data_buffer_;
345 int64 expected_total_size_;
346 int amount_headers_read_;
347 int amount_data_read_;
348 DISALLOW_COPY_AND_ASSIGN(CheckResponseHelper);
351 void AppCacheService::CheckResponseHelper::OnGroupLoaded(
352 AppCacheGroup* group, const GURL& manifest_url) {
353 DCHECK_EQ(manifest_url_, manifest_url);
354 if (!group || !group->newest_complete_cache() || group->is_being_deleted() ||
355 group->is_obsolete()) {
356 AppCacheHistograms::CountCheckResponseResult(
357 AppCacheHistograms::MANIFEST_OUT_OF_DATE);
362 cache_ = group->newest_complete_cache();
363 const AppCacheEntry* entry = cache_->GetEntryWithResponseId(response_id_);
365 if (cache_->cache_id() == cache_id_) {
366 AppCacheHistograms::CountCheckResponseResult(
367 AppCacheHistograms::ENTRY_NOT_FOUND);
368 service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
370 AppCacheHistograms::CountCheckResponseResult(
371 AppCacheHistograms::RESPONSE_OUT_OF_DATE);
377 // Verify that we can read the response info and data.
378 expected_total_size_ = entry->response_size();
379 response_reader_.reset(service_->storage()->CreateResponseReader(
380 manifest_url_, group->group_id(), response_id_));
381 info_buffer_ = new HttpResponseInfoIOBuffer();
382 response_reader_->ReadInfo(
384 base::Bind(&CheckResponseHelper::OnReadInfoComplete,
385 base::Unretained(this)));
388 void AppCacheService::CheckResponseHelper::OnReadInfoComplete(int result) {
390 AppCacheHistograms::CountCheckResponseResult(
391 AppCacheHistograms::READ_HEADERS_ERROR);
392 service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
396 amount_headers_read_ = result;
398 // Start reading the data.
399 data_buffer_ = new net::IOBuffer(kIOBufferSize);
400 response_reader_->ReadData(
403 base::Bind(&CheckResponseHelper::OnReadDataComplete,
404 base::Unretained(this)));
407 void AppCacheService::CheckResponseHelper::OnReadDataComplete(int result) {
409 // Keep reading until we've read thru everything or failed to read.
410 amount_data_read_ += result;
411 response_reader_->ReadData(
414 base::Bind(&CheckResponseHelper::OnReadDataComplete,
415 base::Unretained(this)));
419 AppCacheHistograms::CheckResponseResultType check_result;
421 check_result = AppCacheHistograms::READ_DATA_ERROR;
422 else if (info_buffer_->response_data_size != amount_data_read_ ||
423 expected_total_size_ != amount_data_read_ + amount_headers_read_)
424 check_result = AppCacheHistograms::UNEXPECTED_DATA_SIZE;
426 check_result = AppCacheHistograms::RESPONSE_OK;
427 AppCacheHistograms::CountCheckResponseResult(check_result);
429 if (check_result != AppCacheHistograms::RESPONSE_OK)
430 service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
434 // AppCacheStorageReference ------
436 AppCacheStorageReference::AppCacheStorageReference(
437 scoped_ptr<AppCacheStorage> storage)
438 : storage_(storage.Pass()) {}
439 AppCacheStorageReference::~AppCacheStorageReference() {}
441 // AppCacheService -------
443 AppCacheService::AppCacheService(quota::QuotaManagerProxy* quota_manager_proxy)
444 : appcache_policy_(NULL), quota_client_(NULL), handler_factory_(NULL),
445 quota_manager_proxy_(quota_manager_proxy),
446 request_context_(NULL),
447 force_keep_session_state_(false) {
448 if (quota_manager_proxy_.get()) {
449 quota_client_ = new AppCacheQuotaClient(this);
450 quota_manager_proxy_->RegisterClient(quota_client_);
454 AppCacheService::~AppCacheService() {
455 DCHECK(backends_.empty());
456 std::for_each(pending_helpers_.begin(),
457 pending_helpers_.end(),
458 std::mem_fun(&AsyncHelper::Cancel));
459 STLDeleteElements(&pending_helpers_);
461 quota_client_->NotifyAppCacheDestroyed();
463 // Destroy storage_ first; ~AppCacheStorageImpl accesses other data members
464 // (special_storage_policy_).
468 void AppCacheService::Initialize(const base::FilePath& cache_directory,
469 base::MessageLoopProxy* db_thread,
470 base::MessageLoopProxy* cache_thread) {
471 DCHECK(!storage_.get());
472 cache_directory_ = cache_directory;
473 db_thread_ = db_thread;
474 cache_thread_ = cache_thread;
475 AppCacheStorageImpl* storage = new AppCacheStorageImpl(this);
476 storage->Initialize(cache_directory, db_thread, cache_thread);
477 storage_.reset(storage);
480 void AppCacheService::ScheduleReinitialize() {
481 if (reinit_timer_.IsRunning())
484 // Reinitialization only happens when corruption has been noticed.
485 // We don't want to thrash the disk but we also don't want to
486 // leave the appcache disabled for an indefinite period of time. Some
487 // users never shutdown the browser.
489 const base::TimeDelta kZeroDelta;
490 const base::TimeDelta kOneHour(base::TimeDelta::FromHours(1));
491 const base::TimeDelta k30Seconds(base::TimeDelta::FromSeconds(30));
493 // If the system managed to stay up for long enough, reset the
494 // delay so a new failure won't incur a long wait to get going again.
495 base::TimeDelta up_time = base::Time::Now() - last_reinit_time_;
496 if (next_reinit_delay_ != kZeroDelta && up_time > kOneHour)
497 next_reinit_delay_ = kZeroDelta;
499 reinit_timer_.Start(FROM_HERE, next_reinit_delay_,
500 this, &AppCacheService::Reinitialize);
502 // Adjust the delay for next time.
503 base::TimeDelta increment = std::max(k30Seconds, next_reinit_delay_);
504 next_reinit_delay_ = std::min(next_reinit_delay_ + increment, kOneHour);
507 void AppCacheService::Reinitialize() {
508 AppCacheHistograms::CountReinitAttempt(!last_reinit_time_.is_null());
509 last_reinit_time_ = base::Time::Now();
511 // Inform observers of about this and give them a chance to
512 // defer deletion of the old storage object.
513 scoped_refptr<AppCacheStorageReference>
514 old_storage_ref(new AppCacheStorageReference(storage_.Pass()));
515 FOR_EACH_OBSERVER(Observer, observers_,
516 OnServiceReinitialized(old_storage_ref.get()));
518 Initialize(cache_directory_, db_thread_, cache_thread_);
521 void AppCacheService::CanHandleMainResourceOffline(
523 const GURL& first_party,
524 const net::CompletionCallback& callback) {
525 CanHandleOfflineHelper* helper =
526 new CanHandleOfflineHelper(this, url, first_party, callback);
530 void AppCacheService::GetAllAppCacheInfo(
531 AppCacheInfoCollection* collection,
532 const net::CompletionCallback& callback) {
534 GetInfoHelper* helper = new GetInfoHelper(this, collection, callback);
538 void AppCacheService::DeleteAppCacheGroup(
539 const GURL& manifest_url,
540 const net::CompletionCallback& callback) {
541 DeleteHelper* helper = new DeleteHelper(this, manifest_url, callback);
545 void AppCacheService::DeleteAppCachesForOrigin(
546 const GURL& origin, const net::CompletionCallback& callback) {
547 DeleteOriginHelper* helper = new DeleteOriginHelper(this, origin, callback);
551 void AppCacheService::CheckAppCacheResponse(const GURL& manifest_url,
554 CheckResponseHelper* helper = new CheckResponseHelper(
555 this, manifest_url, cache_id, response_id);
559 void AppCacheService::set_special_storage_policy(
560 quota::SpecialStoragePolicy* policy) {
561 special_storage_policy_ = policy;
564 void AppCacheService::RegisterBackend(
565 AppCacheBackendImpl* backend_impl) {
566 DCHECK(backends_.find(backend_impl->process_id()) == backends_.end());
568 BackendMap::value_type(backend_impl->process_id(), backend_impl));
571 void AppCacheService::UnregisterBackend(
572 AppCacheBackendImpl* backend_impl) {
573 backends_.erase(backend_impl->process_id());
576 } // namespace appcache