summaryrefslogtreecommitdiffstats
path: root/chromium/webkit/browser/appcache/appcache_service_impl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/webkit/browser/appcache/appcache_service_impl.cc')
-rw-r--r--chromium/webkit/browser/appcache/appcache_service_impl.cc578
1 files changed, 578 insertions, 0 deletions
diff --git a/chromium/webkit/browser/appcache/appcache_service_impl.cc b/chromium/webkit/browser/appcache/appcache_service_impl.cc
new file mode 100644
index 00000000000..3091a5cb5fa
--- /dev/null
+++ b/chromium/webkit/browser/appcache/appcache_service_impl.cc
@@ -0,0 +1,578 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/browser/appcache/appcache_service_impl.h"
+
+#include <functional>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "net/base/completion_callback.h"
+#include "net/base/io_buffer.h"
+#include "webkit/browser/appcache/appcache.h"
+#include "webkit/browser/appcache/appcache_backend_impl.h"
+#include "webkit/browser/appcache/appcache_entry.h"
+#include "webkit/browser/appcache/appcache_executable_handler.h"
+#include "webkit/browser/appcache/appcache_histograms.h"
+#include "webkit/browser/appcache/appcache_policy.h"
+#include "webkit/browser/appcache/appcache_quota_client.h"
+#include "webkit/browser/appcache/appcache_response.h"
+#include "webkit/browser/appcache/appcache_service_impl.h"
+#include "webkit/browser/appcache/appcache_storage_impl.h"
+#include "webkit/browser/quota/special_storage_policy.h"
+
+namespace appcache {
+
+namespace {
+
+void DeferredCallback(const net::CompletionCallback& callback, int rv) {
+ callback.Run(rv);
+}
+
+} // namespace
+
+AppCacheInfoCollection::AppCacheInfoCollection() {}
+
+AppCacheInfoCollection::~AppCacheInfoCollection() {}
+
+// AsyncHelper -------
+
+class AppCacheServiceImpl::AsyncHelper
+ : public AppCacheStorage::Delegate {
+ public:
+ AsyncHelper(AppCacheServiceImpl* service,
+ const net::CompletionCallback& callback)
+ : service_(service), callback_(callback) {
+ service_->pending_helpers_.insert(this);
+ }
+
+ virtual ~AsyncHelper() {
+ if (service_)
+ service_->pending_helpers_.erase(this);
+ }
+
+ virtual void Start() = 0;
+ virtual void Cancel();
+
+ protected:
+ void CallCallback(int rv) {
+ if (!callback_.is_null()) {
+ // Defer to guarantee async completion.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&DeferredCallback, callback_, rv));
+ }
+ callback_.Reset();
+ }
+
+ AppCacheServiceImpl* service_;
+ net::CompletionCallback callback_;
+};
+
+void AppCacheServiceImpl::AsyncHelper::Cancel() {
+ if (!callback_.is_null()) {
+ callback_.Run(net::ERR_ABORTED);
+ callback_.Reset();
+ }
+ service_->storage()->CancelDelegateCallbacks(this);
+ service_ = NULL;
+}
+
+// CanHandleOfflineHelper -------
+
+class AppCacheServiceImpl::CanHandleOfflineHelper : AsyncHelper {
+ public:
+ CanHandleOfflineHelper(
+ AppCacheServiceImpl* service, const GURL& url,
+ const GURL& first_party, const net::CompletionCallback& callback)
+ : AsyncHelper(service, callback),
+ url_(url),
+ first_party_(first_party) {
+ }
+
+ virtual void Start() OVERRIDE {
+ AppCachePolicy* policy = service_->appcache_policy();
+ if (policy && !policy->CanLoadAppCache(url_, first_party_)) {
+ CallCallback(net::ERR_FAILED);
+ delete this;
+ return;
+ }
+
+ service_->storage()->FindResponseForMainRequest(url_, GURL(), this);
+ }
+
+ private:
+ // AppCacheStorage::Delegate implementation.
+ virtual void OnMainResponseFound(
+ const GURL& url, const AppCacheEntry& entry,
+ const GURL& fallback_url, const AppCacheEntry& fallback_entry,
+ int64 cache_id, int64 group_id, const GURL& mainfest_url) OVERRIDE;
+
+ GURL url_;
+ GURL first_party_;
+
+ DISALLOW_COPY_AND_ASSIGN(CanHandleOfflineHelper);
+};
+
+void AppCacheServiceImpl::CanHandleOfflineHelper::OnMainResponseFound(
+ const GURL& url, const AppCacheEntry& entry,
+ const GURL& fallback_url, const AppCacheEntry& fallback_entry,
+ int64 cache_id, int64 group_id, const GURL& manifest_url) {
+ bool can = (entry.has_response_id() || fallback_entry.has_response_id());
+ CallCallback(can ? net::OK : net::ERR_FAILED);
+ delete this;
+}
+
+// DeleteHelper -------
+
+class AppCacheServiceImpl::DeleteHelper : public AsyncHelper {
+ public:
+ DeleteHelper(
+ AppCacheServiceImpl* service, const GURL& manifest_url,
+ const net::CompletionCallback& callback)
+ : AsyncHelper(service, callback), manifest_url_(manifest_url) {
+ }
+
+ virtual void Start() OVERRIDE {
+ service_->storage()->LoadOrCreateGroup(manifest_url_, this);
+ }
+
+ private:
+ // AppCacheStorage::Delegate implementation.
+ virtual void OnGroupLoaded(
+ appcache::AppCacheGroup* group, const GURL& manifest_url) OVERRIDE;
+ virtual void OnGroupMadeObsolete(appcache::AppCacheGroup* group,
+ bool success,
+ int response_code) OVERRIDE;
+
+ GURL manifest_url_;
+ DISALLOW_COPY_AND_ASSIGN(DeleteHelper);
+};
+
+void AppCacheServiceImpl::DeleteHelper::OnGroupLoaded(
+ appcache::AppCacheGroup* group, const GURL& manifest_url) {
+ if (group) {
+ group->set_being_deleted(true);
+ group->CancelUpdate();
+ service_->storage()->MakeGroupObsolete(group, this, 0);
+ } else {
+ CallCallback(net::ERR_FAILED);
+ delete this;
+ }
+}
+
+void AppCacheServiceImpl::DeleteHelper::OnGroupMadeObsolete(
+ appcache::AppCacheGroup* group,
+ bool success,
+ int response_code) {
+ CallCallback(success ? net::OK : net::ERR_FAILED);
+ delete this;
+}
+
+// DeleteOriginHelper -------
+
+class AppCacheServiceImpl::DeleteOriginHelper : public AsyncHelper {
+ public:
+ DeleteOriginHelper(
+ AppCacheServiceImpl* service, const GURL& origin,
+ const net::CompletionCallback& callback)
+ : AsyncHelper(service, callback), origin_(origin),
+ num_caches_to_delete_(0), successes_(0), failures_(0) {
+ }
+
+ virtual void Start() OVERRIDE {
+ // We start by listing all caches, continues in OnAllInfo().
+ service_->storage()->GetAllInfo(this);
+ }
+
+ private:
+ // AppCacheStorage::Delegate implementation.
+ virtual void OnAllInfo(AppCacheInfoCollection* collection) OVERRIDE;
+ virtual void OnGroupLoaded(
+ appcache::AppCacheGroup* group, const GURL& manifest_url) OVERRIDE;
+ virtual void OnGroupMadeObsolete(appcache::AppCacheGroup* group,
+ bool success,
+ int response_code) OVERRIDE;
+
+ void CacheCompleted(bool success);
+
+ GURL origin_;
+ int num_caches_to_delete_;
+ int successes_;
+ int failures_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeleteOriginHelper);
+};
+
+void AppCacheServiceImpl::DeleteOriginHelper::OnAllInfo(
+ AppCacheInfoCollection* collection) {
+ if (!collection) {
+ // Failed to get a listing.
+ CallCallback(net::ERR_FAILED);
+ delete this;
+ return;
+ }
+
+ std::map<GURL, AppCacheInfoVector>::iterator found =
+ collection->infos_by_origin.find(origin_);
+ if (found == collection->infos_by_origin.end() || found->second.empty()) {
+ // No caches for this origin.
+ CallCallback(net::OK);
+ delete this;
+ return;
+ }
+
+ // We have some caches to delete.
+ const AppCacheInfoVector& caches_to_delete = found->second;
+ successes_ = 0;
+ failures_ = 0;
+ num_caches_to_delete_ = static_cast<int>(caches_to_delete.size());
+ for (AppCacheInfoVector::const_iterator iter = caches_to_delete.begin();
+ iter != caches_to_delete.end(); ++iter) {
+ service_->storage()->LoadOrCreateGroup(iter->manifest_url, this);
+ }
+}
+
+void AppCacheServiceImpl::DeleteOriginHelper::OnGroupLoaded(
+ appcache::AppCacheGroup* group, const GURL& manifest_url) {
+ if (group) {
+ group->set_being_deleted(true);
+ group->CancelUpdate();
+ service_->storage()->MakeGroupObsolete(group, this, 0);
+ } else {
+ CacheCompleted(false);
+ }
+}
+
+void AppCacheServiceImpl::DeleteOriginHelper::OnGroupMadeObsolete(
+ appcache::AppCacheGroup* group,
+ bool success,
+ int response_code) {
+ CacheCompleted(success);
+}
+
+void AppCacheServiceImpl::DeleteOriginHelper::CacheCompleted(bool success) {
+ if (success)
+ ++successes_;
+ else
+ ++failures_;
+ if ((successes_ + failures_) < num_caches_to_delete_)
+ return;
+
+ CallCallback(!failures_ ? net::OK : net::ERR_FAILED);
+ delete this;
+}
+
+
+// GetInfoHelper -------
+
+class AppCacheServiceImpl::GetInfoHelper : AsyncHelper {
+ public:
+ GetInfoHelper(
+ AppCacheServiceImpl* service, AppCacheInfoCollection* collection,
+ const net::CompletionCallback& callback)
+ : AsyncHelper(service, callback), collection_(collection) {
+ }
+
+ virtual void Start() OVERRIDE {
+ service_->storage()->GetAllInfo(this);
+ }
+
+ private:
+ // AppCacheStorage::Delegate implementation.
+ virtual void OnAllInfo(AppCacheInfoCollection* collection) OVERRIDE;
+
+ scoped_refptr<AppCacheInfoCollection> collection_;
+
+ DISALLOW_COPY_AND_ASSIGN(GetInfoHelper);
+};
+
+void AppCacheServiceImpl::GetInfoHelper::OnAllInfo(
+ AppCacheInfoCollection* collection) {
+ if (collection)
+ collection->infos_by_origin.swap(collection_->infos_by_origin);
+ CallCallback(collection ? net::OK : net::ERR_FAILED);
+ delete this;
+}
+
+// CheckResponseHelper -------
+
+class AppCacheServiceImpl::CheckResponseHelper : AsyncHelper {
+ public:
+ CheckResponseHelper(
+ AppCacheServiceImpl* service, const GURL& manifest_url, int64 cache_id,
+ int64 response_id)
+ : AsyncHelper(service, net::CompletionCallback()),
+ manifest_url_(manifest_url),
+ cache_id_(cache_id),
+ response_id_(response_id),
+ kIOBufferSize(32 * 1024),
+ expected_total_size_(0),
+ amount_headers_read_(0),
+ amount_data_read_(0) {
+ }
+
+ virtual void Start() OVERRIDE {
+ service_->storage()->LoadOrCreateGroup(manifest_url_, this);
+ }
+
+ virtual void Cancel() OVERRIDE {
+ AppCacheHistograms::CountCheckResponseResult(
+ AppCacheHistograms::CHECK_CANCELED);
+ response_reader_.reset();
+ AsyncHelper::Cancel();
+ }
+
+ private:
+ virtual void OnGroupLoaded(AppCacheGroup* group,
+ const GURL& manifest_url) OVERRIDE;
+ void OnReadInfoComplete(int result);
+ void OnReadDataComplete(int result);
+
+ // Inputs describing what to check.
+ GURL manifest_url_;
+ int64 cache_id_;
+ int64 response_id_;
+
+ // Internals used to perform the checks.
+ const int kIOBufferSize;
+ scoped_refptr<AppCache> cache_;
+ scoped_ptr<AppCacheResponseReader> response_reader_;
+ scoped_refptr<HttpResponseInfoIOBuffer> info_buffer_;
+ scoped_refptr<net::IOBuffer> data_buffer_;
+ int64 expected_total_size_;
+ int amount_headers_read_;
+ int amount_data_read_;
+ DISALLOW_COPY_AND_ASSIGN(CheckResponseHelper);
+};
+
+void AppCacheServiceImpl::CheckResponseHelper::OnGroupLoaded(
+ AppCacheGroup* group, const GURL& manifest_url) {
+ DCHECK_EQ(manifest_url_, manifest_url);
+ if (!group || !group->newest_complete_cache() || group->is_being_deleted() ||
+ group->is_obsolete()) {
+ AppCacheHistograms::CountCheckResponseResult(
+ AppCacheHistograms::MANIFEST_OUT_OF_DATE);
+ delete this;
+ return;
+ }
+
+ cache_ = group->newest_complete_cache();
+ const AppCacheEntry* entry = cache_->GetEntryWithResponseId(response_id_);
+ if (!entry) {
+ if (cache_->cache_id() == cache_id_) {
+ AppCacheHistograms::CountCheckResponseResult(
+ AppCacheHistograms::ENTRY_NOT_FOUND);
+ service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
+ } else {
+ AppCacheHistograms::CountCheckResponseResult(
+ AppCacheHistograms::RESPONSE_OUT_OF_DATE);
+ }
+ delete this;
+ return;
+ }
+
+ // Verify that we can read the response info and data.
+ expected_total_size_ = entry->response_size();
+ response_reader_.reset(service_->storage()->CreateResponseReader(
+ manifest_url_, group->group_id(), response_id_));
+ info_buffer_ = new HttpResponseInfoIOBuffer();
+ response_reader_->ReadInfo(
+ info_buffer_.get(),
+ base::Bind(&CheckResponseHelper::OnReadInfoComplete,
+ base::Unretained(this)));
+}
+
+void AppCacheServiceImpl::CheckResponseHelper::OnReadInfoComplete(int result) {
+ if (result < 0) {
+ AppCacheHistograms::CountCheckResponseResult(
+ AppCacheHistograms::READ_HEADERS_ERROR);
+ service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
+ delete this;
+ return;
+ }
+ amount_headers_read_ = result;
+
+ // Start reading the data.
+ data_buffer_ = new net::IOBuffer(kIOBufferSize);
+ response_reader_->ReadData(
+ data_buffer_.get(),
+ kIOBufferSize,
+ base::Bind(&CheckResponseHelper::OnReadDataComplete,
+ base::Unretained(this)));
+}
+
+void AppCacheServiceImpl::CheckResponseHelper::OnReadDataComplete(int result) {
+ if (result > 0) {
+ // Keep reading until we've read thru everything or failed to read.
+ amount_data_read_ += result;
+ response_reader_->ReadData(
+ data_buffer_.get(),
+ kIOBufferSize,
+ base::Bind(&CheckResponseHelper::OnReadDataComplete,
+ base::Unretained(this)));
+ return;
+ }
+
+ AppCacheHistograms::CheckResponseResultType check_result;
+ if (result < 0)
+ check_result = AppCacheHistograms::READ_DATA_ERROR;
+ else if (info_buffer_->response_data_size != amount_data_read_ ||
+ expected_total_size_ != amount_data_read_ + amount_headers_read_)
+ check_result = AppCacheHistograms::UNEXPECTED_DATA_SIZE;
+ else
+ check_result = AppCacheHistograms::RESPONSE_OK;
+ AppCacheHistograms::CountCheckResponseResult(check_result);
+
+ if (check_result != AppCacheHistograms::RESPONSE_OK)
+ service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
+ delete this;
+}
+
+// AppCacheStorageReference ------
+
+AppCacheStorageReference::AppCacheStorageReference(
+ scoped_ptr<AppCacheStorage> storage)
+ : storage_(storage.Pass()) {}
+AppCacheStorageReference::~AppCacheStorageReference() {}
+
+// AppCacheServiceImpl -------
+
+AppCacheServiceImpl::AppCacheServiceImpl(quota::QuotaManagerProxy*
+ quota_manager_proxy)
+ : appcache_policy_(NULL), quota_client_(NULL), handler_factory_(NULL),
+ quota_manager_proxy_(quota_manager_proxy),
+ request_context_(NULL),
+ force_keep_session_state_(false) {
+ if (quota_manager_proxy_.get()) {
+ quota_client_ = new AppCacheQuotaClient(this);
+ quota_manager_proxy_->RegisterClient(quota_client_);
+ }
+}
+
+AppCacheServiceImpl::~AppCacheServiceImpl() {
+ DCHECK(backends_.empty());
+ std::for_each(pending_helpers_.begin(),
+ pending_helpers_.end(),
+ std::mem_fun(&AsyncHelper::Cancel));
+ STLDeleteElements(&pending_helpers_);
+ if (quota_client_)
+ quota_client_->NotifyAppCacheDestroyed();
+
+ // Destroy storage_ first; ~AppCacheStorageImpl accesses other data members
+ // (special_storage_policy_).
+ storage_.reset();
+}
+
+void AppCacheServiceImpl::Initialize(const base::FilePath& cache_directory,
+ base::MessageLoopProxy* db_thread,
+ base::MessageLoopProxy* cache_thread) {
+ DCHECK(!storage_.get());
+ cache_directory_ = cache_directory;
+ db_thread_ = db_thread;
+ cache_thread_ = cache_thread;
+ AppCacheStorageImpl* storage = new AppCacheStorageImpl(this);
+ storage->Initialize(cache_directory, db_thread, cache_thread);
+ storage_.reset(storage);
+}
+
+void AppCacheServiceImpl::ScheduleReinitialize() {
+ if (reinit_timer_.IsRunning())
+ return;
+
+ // Reinitialization only happens when corruption has been noticed.
+ // We don't want to thrash the disk but we also don't want to
+ // leave the appcache disabled for an indefinite period of time. Some
+ // users never shutdown the browser.
+
+ const base::TimeDelta kZeroDelta;
+ const base::TimeDelta kOneHour(base::TimeDelta::FromHours(1));
+ const base::TimeDelta k30Seconds(base::TimeDelta::FromSeconds(30));
+
+ // If the system managed to stay up for long enough, reset the
+ // delay so a new failure won't incur a long wait to get going again.
+ base::TimeDelta up_time = base::Time::Now() - last_reinit_time_;
+ if (next_reinit_delay_ != kZeroDelta && up_time > kOneHour)
+ next_reinit_delay_ = kZeroDelta;
+
+ reinit_timer_.Start(FROM_HERE, next_reinit_delay_,
+ this, &AppCacheServiceImpl::Reinitialize);
+
+ // Adjust the delay for next time.
+ base::TimeDelta increment = std::max(k30Seconds, next_reinit_delay_);
+ next_reinit_delay_ = std::min(next_reinit_delay_ + increment, kOneHour);
+}
+
+void AppCacheServiceImpl::Reinitialize() {
+ AppCacheHistograms::CountReinitAttempt(!last_reinit_time_.is_null());
+ last_reinit_time_ = base::Time::Now();
+
+ // Inform observers of about this and give them a chance to
+ // defer deletion of the old storage object.
+ scoped_refptr<AppCacheStorageReference>
+ old_storage_ref(new AppCacheStorageReference(storage_.Pass()));
+ FOR_EACH_OBSERVER(Observer, observers_,
+ OnServiceReinitialized(old_storage_ref.get()));
+
+ Initialize(cache_directory_, db_thread_, cache_thread_);
+}
+
+void AppCacheServiceImpl::CanHandleMainResourceOffline(
+ const GURL& url,
+ const GURL& first_party,
+ const net::CompletionCallback& callback) {
+ CanHandleOfflineHelper* helper =
+ new CanHandleOfflineHelper(this, url, first_party, callback);
+ helper->Start();
+}
+
+void AppCacheServiceImpl::GetAllAppCacheInfo(
+ AppCacheInfoCollection* collection,
+ const net::CompletionCallback& callback) {
+ DCHECK(collection);
+ GetInfoHelper* helper = new GetInfoHelper(this, collection, callback);
+ helper->Start();
+}
+
+void AppCacheServiceImpl::DeleteAppCacheGroup(
+ const GURL& manifest_url,
+ const net::CompletionCallback& callback) {
+ DeleteHelper* helper = new DeleteHelper(this, manifest_url, callback);
+ helper->Start();
+}
+
+void AppCacheServiceImpl::DeleteAppCachesForOrigin(
+ const GURL& origin, const net::CompletionCallback& callback) {
+ DeleteOriginHelper* helper = new DeleteOriginHelper(this, origin, callback);
+ helper->Start();
+}
+
+void AppCacheServiceImpl::CheckAppCacheResponse(const GURL& manifest_url,
+ int64 cache_id,
+ int64 response_id) {
+ CheckResponseHelper* helper = new CheckResponseHelper(
+ this, manifest_url, cache_id, response_id);
+ helper->Start();
+}
+
+void AppCacheServiceImpl::set_special_storage_policy(
+ quota::SpecialStoragePolicy* policy) {
+ special_storage_policy_ = policy;
+}
+
+void AppCacheServiceImpl::RegisterBackend(
+ AppCacheBackendImpl* backend_impl) {
+ DCHECK(backends_.find(backend_impl->process_id()) == backends_.end());
+ backends_.insert(
+ BackendMap::value_type(backend_impl->process_id(), backend_impl));
+}
+
+void AppCacheServiceImpl::UnregisterBackend(
+ AppCacheBackendImpl* backend_impl) {
+ backends_.erase(backend_impl->process_id());
+}
+
+} // namespace appcache