summaryrefslogtreecommitdiffstats
path: root/content
diff options
context:
space:
mode:
authorpilgrim@chromium.org <pilgrim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-25 20:57:55 +0000
committerpilgrim@chromium.org <pilgrim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-25 20:57:55 +0000
commit98d6d4562142f682d3bc8419c0f29a4f7fd0f263 (patch)
tree005b061e143cefc1d2c890a700b28edb57b8573d /content
parent0282b03c86d52533c58af2c6e8de4c7bc7a89155 (diff)
downloadchromium_src-98d6d4562142f682d3bc8419c0f29a4f7fd0f263.zip
chromium_src-98d6d4562142f682d3bc8419c0f29a4f7fd0f263.tar.gz
chromium_src-98d6d4562142f682d3bc8419c0f29a4f7fd0f263.tar.bz2
Move all remaining appcache-related code to content namespace
This introduces NO FUNCTIONAL CHANGES. It is strictly a file move and namespace change. BUG=338338 R=michaeln@chromium.org TBR=cevans, darin Review URL: https://codereview.chromium.org/344493002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@279808 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
-rw-r--r--content/browser/BUILD.gn6
-rw-r--r--content/browser/appcache/DEPS3
-rw-r--r--content/browser/appcache/appcache.cc326
-rw-r--r--content/browser/appcache/appcache.h214
-rw-r--r--content/browser/appcache/appcache_backend_impl.cc182
-rw-r--r--content/browser/appcache/appcache_backend_impl.h75
-rw-r--r--content/browser/appcache/appcache_database.cc1226
-rw-r--r--content/browser/appcache/appcache_database.h254
-rw-r--r--content/browser/appcache/appcache_database_unittest.cc10
-rw-r--r--content/browser/appcache/appcache_disk_cache.cc351
-rw-r--r--content/browser/appcache/appcache_disk_cache.h104
-rw-r--r--content/browser/appcache/appcache_disk_cache_unittest.cc4
-rw-r--r--content/browser/appcache/appcache_dispatcher_host.cc12
-rw-r--r--content/browser/appcache/appcache_dispatcher_host.h14
-rw-r--r--content/browser/appcache/appcache_entry.h67
-rw-r--r--content/browser/appcache/appcache_executable_handler.h54
-rw-r--r--content/browser/appcache/appcache_frontend_proxy.cc12
-rw-r--r--content/browser/appcache/appcache_frontend_proxy.h14
-rw-r--r--content/browser/appcache/appcache_group.cc264
-rw-r--r--content/browser/appcache/appcache_group.h177
-rw-r--r--content/browser/appcache/appcache_group_unittest.cc41
-rw-r--r--content/browser/appcache/appcache_histograms.cc154
-rw-r--r--content/browser/appcache/appcache_histograms.h63
-rw-r--r--content/browser/appcache/appcache_host.cc546
-rw-r--r--content/browser/appcache/appcache_host.h338
-rw-r--r--content/browser/appcache/appcache_host_unittest.cc54
-rw-r--r--content/browser/appcache/appcache_interceptor.cc19
-rw-r--r--content/browser/appcache/appcache_interceptor.h8
-rw-r--r--content/browser/appcache/appcache_policy.h32
-rw-r--r--content/browser/appcache/appcache_quota_client.cc253
-rw-r--r--content/browser/appcache/appcache_quota_client.h99
-rw-r--r--content/browser/appcache/appcache_quota_client_unittest.cc4
-rw-r--r--content/browser/appcache/appcache_request_handler.cc402
-rw-r--r--content/browser/appcache/appcache_request_handler.h152
-rw-r--r--content/browser/appcache/appcache_request_handler_unittest.cc29
-rw-r--r--content/browser/appcache/appcache_response.cc423
-rw-r--r--content/browser/appcache/appcache_response.h266
-rw-r--r--content/browser/appcache/appcache_response_unittest.cc7
-rw-r--r--content/browser/appcache/appcache_service_impl.cc578
-rw-r--r--content/browser/appcache/appcache_service_impl.h227
-rw-r--r--content/browser/appcache/appcache_service_unittest.cc16
-rw-r--r--content/browser/appcache/appcache_storage.cc136
-rw-r--r--content/browser/appcache/appcache_storage.h330
-rw-r--r--content/browser/appcache/appcache_storage_impl.cc1860
-rw-r--r--content/browser/appcache/appcache_storage_impl.h181
-rw-r--r--content/browser/appcache/appcache_storage_impl_unittest.cc67
-rw-r--r--content/browser/appcache/appcache_storage_unittest.cc14
-rw-r--r--content/browser/appcache/appcache_unittest.cc82
-rw-r--r--content/browser/appcache/appcache_update_job.cc1611
-rw-r--r--content/browser/appcache/appcache_update_job.h352
-rw-r--r--content/browser/appcache/appcache_update_job_unittest.cc50
-rw-r--r--content/browser/appcache/appcache_url_request_job.cc449
-rw-r--r--content/browser/appcache/appcache_url_request_job.h182
-rw-r--r--content/browser/appcache/appcache_url_request_job_unittest.cc14
-rw-r--r--content/browser/appcache/appcache_working_set.cc80
-rw-r--r--content/browser/appcache/appcache_working_set.h76
-rw-r--r--content/browser/appcache/chrome_appcache_service.cc2
-rw-r--r--content/browser/appcache/chrome_appcache_service.h8
-rw-r--r--content/browser/appcache/chrome_appcache_service_unittest.cc8
-rw-r--r--content/browser/appcache/manifest_parser.cc382
-rw-r--r--content/browser/appcache/manifest_parser.h72
-rw-r--r--content/browser/appcache/manifest_parser_unittest.cc22
-rw-r--r--content/browser/appcache/mock_appcache_policy.h4
-rw-r--r--content/browser/appcache/mock_appcache_service.h4
-rw-r--r--content/browser/appcache/mock_appcache_storage.cc17
-rw-r--r--content/browser/appcache/mock_appcache_storage.h21
-rw-r--r--content/browser/appcache/mock_appcache_storage_unittest.cc26
-rw-r--r--content/browser/appcache/view_appcache_internals_job.cc24
-rw-r--r--content/browser/appcache/view_appcache_internals_job.h6
-rw-r--r--content/browser/browser_context.cc4
-rw-r--r--content/browser/cross_site_transfer_browsertest.cc2
-rw-r--r--content/browser/loader/resource_dispatcher_host_impl.cc2
-rw-r--r--content/browser/loader/resource_dispatcher_host_unittest.cc6
-rw-r--r--content/browser/quota/mock_quota_manager_unittest.cc2
-rw-r--r--content/browser/service_worker/service_worker_disk_cache.cc7
-rw-r--r--content/browser/service_worker/service_worker_disk_cache.h18
-rw-r--r--content/browser/service_worker/service_worker_request_handler.cc8
-rw-r--r--content/browser/storage_partition_impl_map.cc1
-rw-r--r--content/browser/transition_browsertest.cc2
-rw-r--r--content/browser/webui/url_data_manager_backend.cc2
-rw-r--r--content/browser/webui/url_data_manager_backend.h4
-rw-r--r--content/child/appcache/appcache_backend_proxy.cc6
-rw-r--r--content/child/appcache/appcache_backend_proxy.h8
-rw-r--r--content/child/appcache/appcache_dispatcher.cc12
-rw-r--r--content/child/appcache/appcache_dispatcher.h14
-rw-r--r--content/child/appcache/appcache_frontend_impl.cc66
-rw-r--r--content/child/appcache/appcache_frontend_impl.h14
-rw-r--r--content/child/appcache/web_application_cache_host_impl.cc84
-rw-r--r--content/child/appcache/web_application_cache_host_impl.h22
-rw-r--r--content/child/resource_dispatcher_unittest.cc4
-rw-r--r--content/common/appcache_interfaces.cc139
-rw-r--r--content/common/appcache_interfaces.h188
-rw-r--r--content/common/appcache_messages.h32
-rw-r--r--content/content_browser.gypi45
-rw-r--r--content/content_common.gypi3
-rw-r--r--content/public/browser/appcache_service.h65
-rw-r--r--content/public/browser/resource_context.h2
-rw-r--r--content/public/browser/resource_dispatcher_host_delegate.cc2
-rw-r--r--content/public/browser/resource_dispatcher_host_delegate.h4
-rw-r--r--content/public/browser/storage_partition.h4
-rw-r--r--content/public/common/appcache_info.h50
-rw-r--r--content/public/common/resource_response_info.cc4
-rw-r--r--content/renderer/renderer_webapplicationcachehost_impl.cc5
-rw-r--r--content/renderer/renderer_webapplicationcachehost_impl.h6
-rw-r--r--content/test/appcache_test_helper.cc28
-rw-r--r--content/test/appcache_test_helper.h16
106 files changed, 13431 insertions, 610 deletions
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 7ec025c..c3e3226 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -6,6 +6,12 @@ import("//build/config/features.gni")
import("//build/config/ui.gni")
import("//content/browser/browser.gni")
+config("storage_config") {
+ if (is_android) {
+ defines = [ "APPCACHE_USE_SIMPLE_CACHE" ]
+ }
+}
+
source_set("browser") {
# Only targets in the content tree can depend directly on this target.
visibility = [ "//content/*" ]
diff --git a/content/browser/appcache/DEPS b/content/browser/appcache/DEPS
new file mode 100644
index 0000000..8608d5f
--- /dev/null
+++ b/content/browser/appcache/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+net/disk_cache",
+]
diff --git a/content/browser/appcache/appcache.cc b/content/browser/appcache/appcache.cc
new file mode 100644
index 0000000..7b23f64
--- /dev/null
+++ b/content/browser/appcache/appcache.cc
@@ -0,0 +1,326 @@
+// Copyright (c) 2012 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 "content/browser/appcache/appcache.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "content/browser/appcache/appcache_executable_handler.h"
+#include "content/browser/appcache/appcache_group.h"
+#include "content/browser/appcache/appcache_host.h"
+#include "content/browser/appcache/appcache_storage.h"
+#include "content/common/appcache_interfaces.h"
+
+namespace content {
+
+AppCache::AppCache(AppCacheStorage* storage, int64 cache_id)
+ : cache_id_(cache_id),
+ owning_group_(NULL),
+ online_whitelist_all_(false),
+ is_complete_(false),
+ cache_size_(0),
+ storage_(storage) {
+ storage_->working_set()->AddCache(this);
+}
+
+AppCache::~AppCache() {
+ DCHECK(associated_hosts_.empty());
+ if (owning_group_.get()) {
+ DCHECK(is_complete_);
+ owning_group_->RemoveCache(this);
+ }
+ DCHECK(!owning_group_.get());
+ storage_->working_set()->RemoveCache(this);
+ STLDeleteContainerPairSecondPointers(
+ executable_handlers_.begin(), executable_handlers_.end());
+}
+
+void AppCache::UnassociateHost(AppCacheHost* host) {
+ associated_hosts_.erase(host);
+}
+
+void AppCache::AddEntry(const GURL& url, const AppCacheEntry& entry) {
+ DCHECK(entries_.find(url) == entries_.end());
+ entries_.insert(EntryMap::value_type(url, entry));
+ cache_size_ += entry.response_size();
+}
+
+bool AppCache::AddOrModifyEntry(const GURL& url, const AppCacheEntry& entry) {
+ std::pair<EntryMap::iterator, bool> ret =
+ entries_.insert(EntryMap::value_type(url, entry));
+
+ // Entry already exists. Merge the types of the new and existing entries.
+ if (!ret.second)
+ ret.first->second.add_types(entry.types());
+ else
+ cache_size_ += entry.response_size(); // New entry. Add to cache size.
+ return ret.second;
+}
+
+void AppCache::RemoveEntry(const GURL& url) {
+ EntryMap::iterator found = entries_.find(url);
+ DCHECK(found != entries_.end());
+ cache_size_ -= found->second.response_size();
+ entries_.erase(found);
+}
+
+AppCacheEntry* AppCache::GetEntry(const GURL& url) {
+ EntryMap::iterator it = entries_.find(url);
+ return (it != entries_.end()) ? &(it->second) : NULL;
+}
+
+const AppCacheEntry* AppCache::GetEntryAndUrlWithResponseId(
+ int64 response_id, GURL* optional_url_out) {
+ for (EntryMap::const_iterator iter = entries_.begin();
+ iter != entries_.end(); ++iter) {
+ if (iter->second.response_id() == response_id) {
+ if (optional_url_out)
+ *optional_url_out = iter->first;
+ return &iter->second;
+ }
+ }
+ return NULL;
+}
+
+AppCacheExecutableHandler* AppCache::GetExecutableHandler(int64 response_id) {
+ HandlerMap::const_iterator found = executable_handlers_.find(response_id);
+ if (found != executable_handlers_.end())
+ return found->second;
+ return NULL;
+}
+
+AppCacheExecutableHandler* AppCache::GetOrCreateExecutableHandler(
+ int64 response_id, net::IOBuffer* handler_source) {
+ AppCacheExecutableHandler* handler = GetExecutableHandler(response_id);
+ if (handler)
+ return handler;
+
+ GURL handler_url;
+ const AppCacheEntry* entry = GetEntryAndUrlWithResponseId(
+ response_id, &handler_url);
+ if (!entry || !entry->IsExecutable())
+ return NULL;
+
+ DCHECK(storage_->service()->handler_factory());
+ scoped_ptr<AppCacheExecutableHandler> own_ptr =
+ storage_->service()->handler_factory()->
+ CreateHandler(handler_url, handler_source);
+ handler = own_ptr.release();
+ if (!handler)
+ return NULL;
+ executable_handlers_[response_id] = handler;
+ return handler;
+}
+
+GURL AppCache::GetNamespaceEntryUrl(const AppCacheNamespaceVector& namespaces,
+ const GURL& namespace_url) const {
+ size_t count = namespaces.size();
+ for (size_t i = 0; i < count; ++i) {
+ if (namespaces[i].namespace_url == namespace_url)
+ return namespaces[i].target_url;
+ }
+ NOTREACHED();
+ return GURL();
+}
+
+namespace {
+bool SortNamespacesByLength(
+ const AppCacheNamespace& lhs, const AppCacheNamespace& rhs) {
+ return lhs.namespace_url.spec().length() > rhs.namespace_url.spec().length();
+}
+}
+
+void AppCache::InitializeWithManifest(Manifest* manifest) {
+ DCHECK(manifest);
+ intercept_namespaces_.swap(manifest->intercept_namespaces);
+ fallback_namespaces_.swap(manifest->fallback_namespaces);
+ online_whitelist_namespaces_.swap(manifest->online_whitelist_namespaces);
+ online_whitelist_all_ = manifest->online_whitelist_all;
+
+ // Sort the namespaces by url string length, longest to shortest,
+ // since longer matches trump when matching a url to a namespace.
+ std::sort(intercept_namespaces_.begin(), intercept_namespaces_.end(),
+ SortNamespacesByLength);
+ std::sort(fallback_namespaces_.begin(), fallback_namespaces_.end(),
+ SortNamespacesByLength);
+}
+
+void AppCache::InitializeWithDatabaseRecords(
+ const AppCacheDatabase::CacheRecord& cache_record,
+ const std::vector<AppCacheDatabase::EntryRecord>& entries,
+ const std::vector<AppCacheDatabase::NamespaceRecord>& intercepts,
+ const std::vector<AppCacheDatabase::NamespaceRecord>& fallbacks,
+ const std::vector<AppCacheDatabase::OnlineWhiteListRecord>& whitelists) {
+ DCHECK(cache_id_ == cache_record.cache_id);
+ online_whitelist_all_ = cache_record.online_wildcard;
+ update_time_ = cache_record.update_time;
+
+ for (size_t i = 0; i < entries.size(); ++i) {
+ const AppCacheDatabase::EntryRecord& entry = entries.at(i);
+ AddEntry(entry.url, AppCacheEntry(entry.flags, entry.response_id,
+ entry.response_size));
+ }
+ DCHECK(cache_size_ == cache_record.cache_size);
+
+ for (size_t i = 0; i < intercepts.size(); ++i)
+ intercept_namespaces_.push_back(intercepts.at(i).namespace_);
+
+ for (size_t i = 0; i < fallbacks.size(); ++i)
+ fallback_namespaces_.push_back(fallbacks.at(i).namespace_);
+
+ // Sort the fallback namespaces by url string length, longest to shortest,
+ // since longer matches trump when matching a url to a namespace.
+ std::sort(intercept_namespaces_.begin(), intercept_namespaces_.end(),
+ SortNamespacesByLength);
+ std::sort(fallback_namespaces_.begin(), fallback_namespaces_.end(),
+ SortNamespacesByLength);
+
+ for (size_t i = 0; i < whitelists.size(); ++i) {
+ const AppCacheDatabase::OnlineWhiteListRecord& record = whitelists.at(i);
+ online_whitelist_namespaces_.push_back(
+ AppCacheNamespace(APPCACHE_NETWORK_NAMESPACE,
+ record.namespace_url,
+ GURL(),
+ record.is_pattern));
+ }
+}
+
+void AppCache::ToDatabaseRecords(
+ const AppCacheGroup* group,
+ AppCacheDatabase::CacheRecord* cache_record,
+ std::vector<AppCacheDatabase::EntryRecord>* entries,
+ std::vector<AppCacheDatabase::NamespaceRecord>* intercepts,
+ std::vector<AppCacheDatabase::NamespaceRecord>* fallbacks,
+ std::vector<AppCacheDatabase::OnlineWhiteListRecord>* whitelists) {
+ DCHECK(group && cache_record && entries && fallbacks && whitelists);
+ DCHECK(entries->empty() && fallbacks->empty() && whitelists->empty());
+
+ cache_record->cache_id = cache_id_;
+ cache_record->group_id = group->group_id();
+ cache_record->online_wildcard = online_whitelist_all_;
+ cache_record->update_time = update_time_;
+ cache_record->cache_size = 0;
+
+ for (EntryMap::const_iterator iter = entries_.begin();
+ iter != entries_.end(); ++iter) {
+ entries->push_back(AppCacheDatabase::EntryRecord());
+ AppCacheDatabase::EntryRecord& record = entries->back();
+ record.url = iter->first;
+ record.cache_id = cache_id_;
+ record.flags = iter->second.types();
+ record.response_id = iter->second.response_id();
+ record.response_size = iter->second.response_size();
+ cache_record->cache_size += record.response_size;
+ }
+
+ GURL origin = group->manifest_url().GetOrigin();
+
+ for (size_t i = 0; i < intercept_namespaces_.size(); ++i) {
+ intercepts->push_back(AppCacheDatabase::NamespaceRecord());
+ AppCacheDatabase::NamespaceRecord& record = intercepts->back();
+ record.cache_id = cache_id_;
+ record.origin = origin;
+ record.namespace_ = intercept_namespaces_[i];
+ }
+
+ for (size_t i = 0; i < fallback_namespaces_.size(); ++i) {
+ fallbacks->push_back(AppCacheDatabase::NamespaceRecord());
+ AppCacheDatabase::NamespaceRecord& record = fallbacks->back();
+ record.cache_id = cache_id_;
+ record.origin = origin;
+ record.namespace_ = fallback_namespaces_[i];
+ }
+
+ for (size_t i = 0; i < online_whitelist_namespaces_.size(); ++i) {
+ whitelists->push_back(AppCacheDatabase::OnlineWhiteListRecord());
+ AppCacheDatabase::OnlineWhiteListRecord& record = whitelists->back();
+ record.cache_id = cache_id_;
+ record.namespace_url = online_whitelist_namespaces_[i].namespace_url;
+ record.is_pattern = online_whitelist_namespaces_[i].is_pattern;
+ }
+}
+
+bool AppCache::FindResponseForRequest(const GURL& url,
+ AppCacheEntry* found_entry, GURL* found_intercept_namespace,
+ AppCacheEntry* found_fallback_entry, GURL* found_fallback_namespace,
+ bool* found_network_namespace) {
+ // Ignore fragments when looking up URL in the cache.
+ GURL url_no_ref;
+ if (url.has_ref()) {
+ GURL::Replacements replacements;
+ replacements.ClearRef();
+ url_no_ref = url.ReplaceComponents(replacements);
+ } else {
+ url_no_ref = url;
+ }
+
+ // 6.6.6 Changes to the networking model
+
+ AppCacheEntry* entry = GetEntry(url_no_ref);
+ if (entry) {
+ *found_entry = *entry;
+ return true;
+ }
+
+ if ((*found_network_namespace = IsInNetworkNamespace(url_no_ref)))
+ return true;
+
+ const AppCacheNamespace* intercept_namespace =
+ FindInterceptNamespace(url_no_ref);
+ if (intercept_namespace) {
+ entry = GetEntry(intercept_namespace->target_url);
+ DCHECK(entry);
+ *found_entry = *entry;
+ *found_intercept_namespace = intercept_namespace->namespace_url;
+ return true;
+ }
+
+ const AppCacheNamespace* fallback_namespace =
+ FindFallbackNamespace(url_no_ref);
+ if (fallback_namespace) {
+ entry = GetEntry(fallback_namespace->target_url);
+ DCHECK(entry);
+ *found_fallback_entry = *entry;
+ *found_fallback_namespace = fallback_namespace->namespace_url;
+ return true;
+ }
+
+ *found_network_namespace = online_whitelist_all_;
+ return *found_network_namespace;
+}
+
+
+void AppCache::ToResourceInfoVector(AppCacheResourceInfoVector* infos) const {
+ DCHECK(infos && infos->empty());
+ for (EntryMap::const_iterator iter = entries_.begin();
+ iter != entries_.end(); ++iter) {
+ infos->push_back(AppCacheResourceInfo());
+ AppCacheResourceInfo& info = infos->back();
+ info.url = iter->first;
+ info.is_master = iter->second.IsMaster();
+ info.is_manifest = iter->second.IsManifest();
+ info.is_intercept = iter->second.IsIntercept();
+ info.is_fallback = iter->second.IsFallback();
+ info.is_foreign = iter->second.IsForeign();
+ info.is_explicit = iter->second.IsExplicit();
+ info.size = iter->second.response_size();
+ info.response_id = iter->second.response_id();
+ }
+}
+
+// static
+const AppCacheNamespace* AppCache::FindNamespace(
+ const AppCacheNamespaceVector& namespaces,
+ const GURL& url) {
+ size_t count = namespaces.size();
+ for (size_t i = 0; i < count; ++i) {
+ if (namespaces[i].IsMatch(url))
+ return &namespaces[i];
+ }
+ return NULL;
+}
+
+} // namespace content
diff --git a/content/browser/appcache/appcache.h b/content/browser/appcache/appcache.h
new file mode 100644
index 0000000..d449139
--- /dev/null
+++ b/content/browser/appcache/appcache.h
@@ -0,0 +1,214 @@
+// Copyright (c) 2012 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.
+
+#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_H_
+#define CONTENT_BROWSER_APPCACHE_APPCACHE_H_
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+#include "content/browser/appcache/appcache_database.h"
+#include "content/browser/appcache/appcache_entry.h"
+#include "content/browser/appcache/manifest_parser.h"
+#include "content/common/content_export.h"
+#include "url/gurl.h"
+
+namespace net {
+class IOBuffer;
+}
+
+namespace content {
+FORWARD_DECLARE_TEST(AppCacheTest, InitializeWithManifest);
+FORWARD_DECLARE_TEST(AppCacheTest, ToFromDatabaseRecords);
+class AppCacheTest;
+class AppCacheStorageImplTest;
+class AppCacheUpdateJobTest;
+}
+
+namespace content {
+
+class AppCacheExecutableHandler;
+class AppCacheGroup;
+class AppCacheHost;
+class AppCacheStorage;
+
+// Set of cached resources for an application. A cache exists as long as a
+// host is associated with it, the cache is in an appcache group or the
+// cache is being created during an appcache upate.
+class CONTENT_EXPORT AppCache
+ : public base::RefCounted<AppCache> {
+ public:
+ typedef std::map<GURL, AppCacheEntry> EntryMap;
+ typedef std::set<AppCacheHost*> AppCacheHosts;
+
+ AppCache(AppCacheStorage* storage, int64 cache_id);
+
+ int64 cache_id() const { return cache_id_; }
+
+ AppCacheGroup* owning_group() const { return owning_group_.get(); }
+
+ bool is_complete() const { return is_complete_; }
+ void set_complete(bool value) { is_complete_ = value; }
+
+ // Adds a new entry. Entry must not already be in cache.
+ void AddEntry(const GURL& url, const AppCacheEntry& entry);
+
+ // Adds a new entry or modifies an existing entry by merging the types
+ // of the new entry with the existing entry. Returns true if a new entry
+ // is added, false if the flags are merged into an existing entry.
+ bool AddOrModifyEntry(const GURL& url, const AppCacheEntry& entry);
+
+ // Removes an entry from the EntryMap, the URL must be in the set.
+ void RemoveEntry(const GURL& url);
+
+ // Do not store or delete the returned ptr, they're owned by 'this'.
+ AppCacheEntry* GetEntry(const GURL& url);
+ const AppCacheEntry* GetEntryWithResponseId(int64 response_id) {
+ return GetEntryAndUrlWithResponseId(response_id, NULL);
+ }
+ const AppCacheEntry* GetEntryAndUrlWithResponseId(
+ int64 response_id, GURL* optional_url);
+ const EntryMap& entries() const { return entries_; }
+
+ // The AppCache owns the collection of executable handlers that have
+ // been started for this instance. The getter looks up an existing
+ // handler returning null if not found, the GetOrCreate method will
+ // cons one up if not found.
+ // Do not store the returned ptrs, they're owned by 'this'.
+ AppCacheExecutableHandler* GetExecutableHandler(int64 response_id);
+ AppCacheExecutableHandler* GetOrCreateExecutableHandler(
+ int64 response_id, net::IOBuffer* handler_source);
+
+ // Returns the URL of the resource used as entry for 'namespace_url'.
+ GURL GetFallbackEntryUrl(const GURL& namespace_url) const {
+ return GetNamespaceEntryUrl(fallback_namespaces_, namespace_url);
+ }
+ GURL GetInterceptEntryUrl(const GURL& namespace_url) const {
+ return GetNamespaceEntryUrl(intercept_namespaces_, namespace_url);
+ }
+
+ AppCacheHosts& associated_hosts() { return associated_hosts_; }
+
+ bool IsNewerThan(AppCache* cache) const {
+ // TODO(michaeln): revisit, the system clock can be set
+ // back in time which would confuse this logic.
+ if (update_time_ > cache->update_time_)
+ return true;
+
+ // Tie breaker. Newer caches have a larger cache ID.
+ if (update_time_ == cache->update_time_)
+ return cache_id_ > cache->cache_id_;
+
+ return false;
+ }
+
+ base::Time update_time() const { return update_time_; }
+
+ int64 cache_size() const { return cache_size_; }
+
+ void set_update_time(base::Time ticks) { update_time_ = ticks; }
+
+ // Initializes the cache with information in the manifest.
+ // Do not use the manifest after this call.
+ void InitializeWithManifest(Manifest* manifest);
+
+ // Initializes the cache with the information in the database records.
+ void InitializeWithDatabaseRecords(
+ const AppCacheDatabase::CacheRecord& cache_record,
+ const std::vector<AppCacheDatabase::EntryRecord>& entries,
+ const std::vector<AppCacheDatabase::NamespaceRecord>& intercepts,
+ const std::vector<AppCacheDatabase::NamespaceRecord>& fallbacks,
+ const std::vector<AppCacheDatabase::OnlineWhiteListRecord>& whitelists);
+
+ // Returns the database records to be stored in the AppCacheDatabase
+ // to represent this cache.
+ void ToDatabaseRecords(
+ const AppCacheGroup* group,
+ AppCacheDatabase::CacheRecord* cache_record,
+ std::vector<AppCacheDatabase::EntryRecord>* entries,
+ std::vector<AppCacheDatabase::NamespaceRecord>* intercepts,
+ std::vector<AppCacheDatabase::NamespaceRecord>* fallbacks,
+ std::vector<AppCacheDatabase::OnlineWhiteListRecord>* whitelists);
+
+ bool FindResponseForRequest(const GURL& url,
+ AppCacheEntry* found_entry, GURL* found_intercept_namespace,
+ AppCacheEntry* found_fallback_entry, GURL* found_fallback_namespace,
+ bool* found_network_namespace);
+
+ // Populates the 'infos' vector with an element per entry in the appcache.
+ void ToResourceInfoVector(AppCacheResourceInfoVector* infos) const;
+
+ static const AppCacheNamespace* FindNamespace(
+ const AppCacheNamespaceVector& namespaces,
+ const GURL& url);
+
+ private:
+ friend class AppCacheGroup;
+ friend class AppCacheHost;
+ friend class content::AppCacheTest;
+ friend class content::AppCacheStorageImplTest;
+ friend class content::AppCacheUpdateJobTest;
+ friend class base::RefCounted<AppCache>;
+
+ ~AppCache();
+
+ // Use AppCacheGroup::Add/RemoveCache() to manipulate owning group.
+ void set_owning_group(AppCacheGroup* group) { owning_group_ = group; }
+
+ // FindResponseForRequest helpers
+ const AppCacheNamespace* FindInterceptNamespace(const GURL& url) {
+ return FindNamespace(intercept_namespaces_, url);
+ }
+ const AppCacheNamespace* FindFallbackNamespace(const GURL& url) {
+ return FindNamespace(fallback_namespaces_, url);
+ }
+ bool IsInNetworkNamespace(const GURL& url) {
+ return FindNamespace(online_whitelist_namespaces_, url) != NULL;
+ }
+
+ GURL GetNamespaceEntryUrl(const AppCacheNamespaceVector& namespaces,
+ const GURL& namespace_url) const;
+
+ // Use AppCacheHost::Associate*Cache() to manipulate host association.
+ void AssociateHost(AppCacheHost* host) {
+ associated_hosts_.insert(host);
+ }
+ void UnassociateHost(AppCacheHost* host);
+
+ const int64 cache_id_;
+ scoped_refptr<AppCacheGroup> owning_group_;
+ AppCacheHosts associated_hosts_;
+
+ EntryMap entries_; // contains entries of all types
+
+ AppCacheNamespaceVector intercept_namespaces_;
+ AppCacheNamespaceVector fallback_namespaces_;
+ AppCacheNamespaceVector online_whitelist_namespaces_;
+ bool online_whitelist_all_;
+
+ bool is_complete_;
+
+ // when this cache was last updated
+ base::Time update_time_;
+
+ int64 cache_size_;
+
+ typedef std::map<int64, AppCacheExecutableHandler*> HandlerMap;
+ HandlerMap executable_handlers_;
+
+ // to notify storage when cache is deleted
+ AppCacheStorage* storage_;
+
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheTest, InitializeWithManifest);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheTest, ToFromDatabaseRecords);
+ DISALLOW_COPY_AND_ASSIGN(AppCache);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_APPCACHE_APPCACHE_H_
diff --git a/content/browser/appcache/appcache_backend_impl.cc b/content/browser/appcache/appcache_backend_impl.cc
new file mode 100644
index 0000000..099c54e
--- /dev/null
+++ b/content/browser/appcache/appcache_backend_impl.cc
@@ -0,0 +1,182 @@
+// Copyright (c) 2011 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 "content/browser/appcache/appcache_backend_impl.h"
+
+#include "base/stl_util.h"
+#include "content/browser/appcache/appcache.h"
+#include "content/browser/appcache/appcache_group.h"
+#include "content/browser/appcache/appcache_service_impl.h"
+
+namespace content {
+
+AppCacheBackendImpl::AppCacheBackendImpl()
+ : service_(NULL),
+ frontend_(NULL),
+ process_id_(0) {
+}
+
+AppCacheBackendImpl::~AppCacheBackendImpl() {
+ STLDeleteValues(&hosts_);
+ if (service_)
+ service_->UnregisterBackend(this);
+}
+
+void AppCacheBackendImpl::Initialize(AppCacheServiceImpl* service,
+ AppCacheFrontend* frontend,
+ int process_id) {
+ DCHECK(!service_ && !frontend_ && frontend && service);
+ service_ = service;
+ frontend_ = frontend;
+ process_id_ = process_id;
+ service_->RegisterBackend(this);
+}
+
+bool AppCacheBackendImpl::RegisterHost(int id) {
+ if (GetHost(id))
+ return false;
+
+ hosts_.insert(
+ HostMap::value_type(id, new AppCacheHost(id, frontend_, service_)));
+ return true;
+}
+
+bool AppCacheBackendImpl::UnregisterHost(int id) {
+ HostMap::iterator found = hosts_.find(id);
+ if (found == hosts_.end())
+ return false;
+
+ delete found->second;
+ hosts_.erase(found);
+ return true;
+}
+
+bool AppCacheBackendImpl::SetSpawningHostId(
+ int host_id,
+ int spawning_host_id) {
+ AppCacheHost* host = GetHost(host_id);
+ if (!host)
+ return false;
+ host->SetSpawningHostId(process_id_, spawning_host_id);
+ return true;
+}
+
+bool AppCacheBackendImpl::SelectCache(
+ int host_id,
+ const GURL& document_url,
+ const int64 cache_document_was_loaded_from,
+ const GURL& manifest_url) {
+ AppCacheHost* host = GetHost(host_id);
+ if (!host)
+ return false;
+
+ host->SelectCache(document_url, cache_document_was_loaded_from,
+ manifest_url);
+ return true;
+}
+
+bool AppCacheBackendImpl::SelectCacheForWorker(
+ int host_id, int parent_process_id, int parent_host_id) {
+ AppCacheHost* host = GetHost(host_id);
+ if (!host)
+ return false;
+
+ host->SelectCacheForWorker(parent_process_id, parent_host_id);
+ return true;
+}
+
+bool AppCacheBackendImpl::SelectCacheForSharedWorker(
+ int host_id, int64 appcache_id) {
+ AppCacheHost* host = GetHost(host_id);
+ if (!host)
+ return false;
+
+ host->SelectCacheForSharedWorker(appcache_id);
+ return true;
+}
+
+bool AppCacheBackendImpl::MarkAsForeignEntry(
+ int host_id,
+ const GURL& document_url,
+ int64 cache_document_was_loaded_from) {
+ AppCacheHost* host = GetHost(host_id);
+ if (!host)
+ return false;
+
+ host->MarkAsForeignEntry(document_url, cache_document_was_loaded_from);
+ return true;
+}
+
+bool AppCacheBackendImpl::GetStatusWithCallback(
+ int host_id, const GetStatusCallback& callback, void* callback_param) {
+ AppCacheHost* host = GetHost(host_id);
+ if (!host)
+ return false;
+
+ host->GetStatusWithCallback(callback, callback_param);
+ return true;
+}
+
+bool AppCacheBackendImpl::StartUpdateWithCallback(
+ int host_id, const StartUpdateCallback& callback, void* callback_param) {
+ AppCacheHost* host = GetHost(host_id);
+ if (!host)
+ return false;
+
+ host->StartUpdateWithCallback(callback, callback_param);
+ return true;
+}
+
+bool AppCacheBackendImpl::SwapCacheWithCallback(
+ int host_id, const SwapCacheCallback& callback, void* callback_param) {
+ AppCacheHost* host = GetHost(host_id);
+ if (!host)
+ return false;
+
+ host->SwapCacheWithCallback(callback, callback_param);
+ return true;
+}
+
+void AppCacheBackendImpl::GetResourceList(
+ int host_id, std::vector<AppCacheResourceInfo>* resource_infos) {
+ AppCacheHost* host = GetHost(host_id);
+ if (!host)
+ return;
+
+ host->GetResourceList(resource_infos);
+}
+
+scoped_ptr<AppCacheHost> AppCacheBackendImpl::TransferHostOut(int host_id) {
+ HostMap::iterator found = hosts_.find(host_id);
+ if (found == hosts_.end()) {
+ NOTREACHED();
+ return scoped_ptr<AppCacheHost>();
+ }
+
+ AppCacheHost* transferree = found->second;
+
+ // Put a new empty host in its place.
+ found->second = new AppCacheHost(host_id, frontend_, service_);
+
+ // We give up ownership.
+ transferree->PrepareForTransfer();
+ return scoped_ptr<AppCacheHost>(transferree);
+}
+
+void AppCacheBackendImpl::TransferHostIn(
+ int new_host_id, scoped_ptr<AppCacheHost> host) {
+ HostMap::iterator found = hosts_.find(new_host_id);
+ if (found == hosts_.end()) {
+ NOTREACHED();
+ return;
+ }
+
+ delete found->second;
+
+ // We take onwership.
+ host->CompleteTransfer(new_host_id, frontend_);
+ found->second = host.release();
+}
+
+} // namespace content
diff --git a/content/browser/appcache/appcache_backend_impl.h b/content/browser/appcache/appcache_backend_impl.h
new file mode 100644
index 0000000..bd6f440
--- /dev/null
+++ b/content/browser/appcache/appcache_backend_impl.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2011 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.
+
+#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_BACKEND_IMPL_H_
+#define CONTENT_BROWSER_APPCACHE_APPCACHE_BACKEND_IMPL_H_
+
+#include "base/containers/hash_tables.h"
+#include "content/browser/appcache/appcache_host.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+class AppCacheServiceImpl;
+
+class CONTENT_EXPORT AppCacheBackendImpl {
+ public:
+ AppCacheBackendImpl();
+ ~AppCacheBackendImpl();
+
+ void Initialize(AppCacheServiceImpl* service,
+ AppCacheFrontend* frontend,
+ int process_id);
+
+ int process_id() const { return process_id_; }
+
+ // Methods to support the AppCacheBackend interface. A false return
+ // value indicates an invalid host_id and that no action was taken
+ // by the backend impl.
+ bool RegisterHost(int host_id);
+ bool UnregisterHost(int host_id);
+ bool SetSpawningHostId(int host_id, int spawning_host_id);
+ bool SelectCache(int host_id,
+ const GURL& document_url,
+ const int64 cache_document_was_loaded_from,
+ const GURL& manifest_url);
+ void GetResourceList(
+ int host_id, std::vector<AppCacheResourceInfo>* resource_infos);
+ bool SelectCacheForWorker(int host_id, int parent_process_id,
+ int parent_host_id);
+ bool SelectCacheForSharedWorker(int host_id, int64 appcache_id);
+ bool MarkAsForeignEntry(int host_id, const GURL& document_url,
+ int64 cache_document_was_loaded_from);
+ bool GetStatusWithCallback(int host_id, const GetStatusCallback& callback,
+ void* callback_param);
+ bool StartUpdateWithCallback(int host_id, const StartUpdateCallback& callback,
+ void* callback_param);
+ bool SwapCacheWithCallback(int host_id, const SwapCacheCallback& callback,
+ void* callback_param);
+
+ // Returns a pointer to a registered host. The backend retains ownership.
+ AppCacheHost* GetHost(int host_id) {
+ HostMap::iterator it = hosts_.find(host_id);
+ return (it != hosts_.end()) ? (it->second) : NULL;
+ }
+
+ typedef base::hash_map<int, AppCacheHost*> HostMap;
+ const HostMap& hosts() { return hosts_; }
+
+ // Methods to support cross site navigations. Hosts are transferred
+ // from process to process accordingly, deparented from the old
+ // processes backend and reparented to the new.
+ scoped_ptr<AppCacheHost> TransferHostOut(int host_id);
+ void TransferHostIn(int new_host_id, scoped_ptr<AppCacheHost> host);
+
+ private:
+ AppCacheServiceImpl* service_;
+ AppCacheFrontend* frontend_;
+ int process_id_;
+ HostMap hosts_;
+};
+
+} // namespace
+
+#endif // CONTENT_BROWSER_APPCACHE_APPCACHE_BACKEND_IMPL_H_
diff --git a/content/browser/appcache/appcache_database.cc b/content/browser/appcache/appcache_database.cc
new file mode 100644
index 0000000..546c1dc
--- /dev/null
+++ b/content/browser/appcache/appcache_database.cc
@@ -0,0 +1,1226 @@
+// Copyright (c) 2012 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 "content/browser/appcache/appcache_database.h"
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/appcache/appcache_entry.h"
+#include "content/browser/appcache/appcache_histograms.h"
+#include "sql/connection.h"
+#include "sql/error_delegate_util.h"
+#include "sql/meta_table.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+
+namespace content {
+
+// Schema -------------------------------------------------------------------
+namespace {
+
+#if defined(APPCACHE_USE_SIMPLE_CACHE)
+const int kCurrentVersion = 6;
+const int kCompatibleVersion = 6;
+#else
+const int kCurrentVersion = 5;
+const int kCompatibleVersion = 5;
+#endif
+
+// A mechanism to run experiments that may affect in data being persisted
+// in different ways such that when the experiment is toggled on/off via
+// cmd line flags, the database gets reset. The active flags are stored at
+// the time of database creation and compared when reopening. If different
+// the database is reset.
+const char kExperimentFlagsKey[] = "ExperimentFlags";
+
+const char kGroupsTable[] = "Groups";
+const char kCachesTable[] = "Caches";
+const char kEntriesTable[] = "Entries";
+const char kNamespacesTable[] = "Namespaces";
+const char kOnlineWhiteListsTable[] = "OnlineWhiteLists";
+const char kDeletableResponseIdsTable[] = "DeletableResponseIds";
+
+struct TableInfo {
+ const char* table_name;
+ const char* columns;
+};
+
+struct IndexInfo {
+ const char* index_name;
+ const char* table_name;
+ const char* columns;
+ bool unique;
+};
+
+const TableInfo kTables[] = {
+ { kGroupsTable,
+ "(group_id INTEGER PRIMARY KEY,"
+ " origin TEXT,"
+ " manifest_url TEXT,"
+ " creation_time INTEGER,"
+ " last_access_time INTEGER)" },
+
+ { kCachesTable,
+ "(cache_id INTEGER PRIMARY KEY,"
+ " group_id INTEGER,"
+ " online_wildcard INTEGER CHECK(online_wildcard IN (0, 1)),"
+ " update_time INTEGER,"
+ " cache_size INTEGER)" }, // intentionally not normalized
+
+ { kEntriesTable,
+ "(cache_id INTEGER,"
+ " url TEXT,"
+ " flags INTEGER,"
+ " response_id INTEGER,"
+ " response_size INTEGER)" },
+
+ { kNamespacesTable,
+ "(cache_id INTEGER,"
+ " origin TEXT," // intentionally not normalized
+ " type INTEGER,"
+ " namespace_url TEXT,"
+ " target_url TEXT,"
+ " is_pattern INTEGER CHECK(is_pattern IN (0, 1)))" },
+
+ { kOnlineWhiteListsTable,
+ "(cache_id INTEGER,"
+ " namespace_url TEXT,"
+ " is_pattern INTEGER CHECK(is_pattern IN (0, 1)))" },
+
+ { kDeletableResponseIdsTable,
+ "(response_id INTEGER NOT NULL)" },
+};
+
+const IndexInfo kIndexes[] = {
+ { "GroupsOriginIndex",
+ kGroupsTable,
+ "(origin)",
+ false },
+
+ { "GroupsManifestIndex",
+ kGroupsTable,
+ "(manifest_url)",
+ true },
+
+ { "CachesGroupIndex",
+ kCachesTable,
+ "(group_id)",
+ false },
+
+ { "EntriesCacheIndex",
+ kEntriesTable,
+ "(cache_id)",
+ false },
+
+ { "EntriesCacheAndUrlIndex",
+ kEntriesTable,
+ "(cache_id, url)",
+ true },
+
+ { "EntriesResponseIdIndex",
+ kEntriesTable,
+ "(response_id)",
+ true },
+
+ { "NamespacesCacheIndex",
+ kNamespacesTable,
+ "(cache_id)",
+ false },
+
+ { "NamespacesOriginIndex",
+ kNamespacesTable,
+ "(origin)",
+ false },
+
+ { "NamespacesCacheAndUrlIndex",
+ kNamespacesTable,
+ "(cache_id, namespace_url)",
+ true },
+
+ { "OnlineWhiteListCacheIndex",
+ kOnlineWhiteListsTable,
+ "(cache_id)",
+ false },
+
+ { "DeletableResponsesIdIndex",
+ kDeletableResponseIdsTable,
+ "(response_id)",
+ true },
+};
+
+const int kTableCount = ARRAYSIZE_UNSAFE(kTables);
+const int kIndexCount = ARRAYSIZE_UNSAFE(kIndexes);
+
+bool CreateTable(sql::Connection* db, const TableInfo& info) {
+ std::string sql("CREATE TABLE ");
+ sql += info.table_name;
+ sql += info.columns;
+ return db->Execute(sql.c_str());
+}
+
+bool CreateIndex(sql::Connection* db, const IndexInfo& info) {
+ std::string sql;
+ if (info.unique)
+ sql += "CREATE UNIQUE INDEX ";
+ else
+ sql += "CREATE INDEX ";
+ sql += info.index_name;
+ sql += " ON ";
+ sql += info.table_name;
+ sql += info.columns;
+ return db->Execute(sql.c_str());
+}
+
+std::string GetActiveExperimentFlags() {
+ if (CommandLine::ForCurrentProcess()->HasSwitch(kEnableExecutableHandlers))
+ return std::string("executableHandlersEnabled");
+ return std::string();
+}
+
+} // anon namespace
+
+// AppCacheDatabase ----------------------------------------------------------
+
+AppCacheDatabase::GroupRecord::GroupRecord()
+ : group_id(0) {
+}
+
+AppCacheDatabase::GroupRecord::~GroupRecord() {
+}
+
+AppCacheDatabase::NamespaceRecord::NamespaceRecord()
+ : cache_id(0) {
+}
+
+AppCacheDatabase::NamespaceRecord::~NamespaceRecord() {
+}
+
+
+AppCacheDatabase::AppCacheDatabase(const base::FilePath& path)
+ : db_file_path_(path),
+ is_disabled_(false),
+ is_recreating_(false),
+ was_corruption_detected_(false) {
+}
+
+AppCacheDatabase::~AppCacheDatabase() {
+}
+
+void AppCacheDatabase::Disable() {
+ VLOG(1) << "Disabling appcache database.";
+ is_disabled_ = true;
+ ResetConnectionAndTables();
+}
+
+int64 AppCacheDatabase::GetOriginUsage(const GURL& origin) {
+ std::vector<CacheRecord> records;
+ if (!FindCachesForOrigin(origin, &records))
+ return 0;
+
+ int64 origin_usage = 0;
+ std::vector<CacheRecord>::const_iterator iter = records.begin();
+ while (iter != records.end()) {
+ origin_usage += iter->cache_size;
+ ++iter;
+ }
+ return origin_usage;
+}
+
+bool AppCacheDatabase::GetAllOriginUsage(std::map<GURL, int64>* usage_map) {
+ std::set<GURL> origins;
+ if (!FindOriginsWithGroups(&origins))
+ return false;
+ for (std::set<GURL>::const_iterator origin = origins.begin();
+ origin != origins.end(); ++origin) {
+ (*usage_map)[*origin] = GetOriginUsage(*origin);
+ }
+ return true;
+}
+
+bool AppCacheDatabase::FindOriginsWithGroups(std::set<GURL>* origins) {
+ DCHECK(origins && origins->empty());
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "SELECT DISTINCT(origin) FROM Groups";
+
+ sql::Statement statement(db_->GetUniqueStatement(kSql));
+
+ while (statement.Step())
+ origins->insert(GURL(statement.ColumnString(0)));
+
+ return statement.Succeeded();
+}
+
+bool AppCacheDatabase::FindLastStorageIds(
+ int64* last_group_id, int64* last_cache_id, int64* last_response_id,
+ int64* last_deletable_response_rowid) {
+ DCHECK(last_group_id && last_cache_id && last_response_id &&
+ last_deletable_response_rowid);
+
+ *last_group_id = 0;
+ *last_cache_id = 0;
+ *last_response_id = 0;
+ *last_deletable_response_rowid = 0;
+
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kMaxGroupIdSql = "SELECT MAX(group_id) FROM Groups";
+ const char* kMaxCacheIdSql = "SELECT MAX(cache_id) FROM Caches";
+ const char* kMaxResponseIdFromEntriesSql =
+ "SELECT MAX(response_id) FROM Entries";
+ const char* kMaxResponseIdFromDeletablesSql =
+ "SELECT MAX(response_id) FROM DeletableResponseIds";
+ const char* kMaxDeletableResponseRowIdSql =
+ "SELECT MAX(rowid) FROM DeletableResponseIds";
+ int64 max_group_id;
+ int64 max_cache_id;
+ int64 max_response_id_from_entries;
+ int64 max_response_id_from_deletables;
+ int64 max_deletable_response_rowid;
+ if (!RunUniqueStatementWithInt64Result(kMaxGroupIdSql, &max_group_id) ||
+ !RunUniqueStatementWithInt64Result(kMaxCacheIdSql, &max_cache_id) ||
+ !RunUniqueStatementWithInt64Result(kMaxResponseIdFromEntriesSql,
+ &max_response_id_from_entries) ||
+ !RunUniqueStatementWithInt64Result(kMaxResponseIdFromDeletablesSql,
+ &max_response_id_from_deletables) ||
+ !RunUniqueStatementWithInt64Result(kMaxDeletableResponseRowIdSql,
+ &max_deletable_response_rowid)) {
+ return false;
+ }
+
+ *last_group_id = max_group_id;
+ *last_cache_id = max_cache_id;
+ *last_response_id = std::max(max_response_id_from_entries,
+ max_response_id_from_deletables);
+ *last_deletable_response_rowid = max_deletable_response_rowid;
+ return true;
+}
+
+bool AppCacheDatabase::FindGroup(int64 group_id, GroupRecord* record) {
+ DCHECK(record);
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "SELECT group_id, origin, manifest_url,"
+ " creation_time, last_access_time"
+ " FROM Groups WHERE group_id = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+
+ statement.BindInt64(0, group_id);
+ if (!statement.Step())
+ return false;
+
+ ReadGroupRecord(statement, record);
+ DCHECK(record->group_id == group_id);
+ return true;
+}
+
+bool AppCacheDatabase::FindGroupForManifestUrl(
+ const GURL& manifest_url, GroupRecord* record) {
+ DCHECK(record);
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "SELECT group_id, origin, manifest_url,"
+ " creation_time, last_access_time"
+ " FROM Groups WHERE manifest_url = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindString(0, manifest_url.spec());
+
+ if (!statement.Step())
+ return false;
+
+ ReadGroupRecord(statement, record);
+ DCHECK(record->manifest_url == manifest_url);
+ return true;
+}
+
+bool AppCacheDatabase::FindGroupsForOrigin(
+ const GURL& origin, std::vector<GroupRecord>* records) {
+ DCHECK(records && records->empty());
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "SELECT group_id, origin, manifest_url,"
+ " creation_time, last_access_time"
+ " FROM Groups WHERE origin = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindString(0, origin.spec());
+
+ while (statement.Step()) {
+ records->push_back(GroupRecord());
+ ReadGroupRecord(statement, &records->back());
+ DCHECK(records->back().origin == origin);
+ }
+
+ return statement.Succeeded();
+}
+
+bool AppCacheDatabase::FindGroupForCache(int64 cache_id, GroupRecord* record) {
+ DCHECK(record);
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "SELECT g.group_id, g.origin, g.manifest_url,"
+ " g.creation_time, g.last_access_time"
+ " FROM Groups g, Caches c"
+ " WHERE c.cache_id = ? AND c.group_id = g.group_id";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, cache_id);
+
+ if (!statement.Step())
+ return false;
+
+ ReadGroupRecord(statement, record);
+ return true;
+}
+
+bool AppCacheDatabase::UpdateGroupLastAccessTime(
+ int64 group_id, base::Time time) {
+ if (!LazyOpen(true))
+ return false;
+
+ const char* kSql =
+ "UPDATE Groups SET last_access_time = ? WHERE group_id = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, time.ToInternalValue());
+ statement.BindInt64(1, group_id);
+
+ return statement.Run() && db_->GetLastChangeCount();
+}
+
+bool AppCacheDatabase::InsertGroup(const GroupRecord* record) {
+ if (!LazyOpen(true))
+ return false;
+
+ const char* kSql =
+ "INSERT INTO Groups"
+ " (group_id, origin, manifest_url, creation_time, last_access_time)"
+ " VALUES(?, ?, ?, ?, ?)";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, record->group_id);
+ statement.BindString(1, record->origin.spec());
+ statement.BindString(2, record->manifest_url.spec());
+ statement.BindInt64(3, record->creation_time.ToInternalValue());
+ statement.BindInt64(4, record->last_access_time.ToInternalValue());
+
+ return statement.Run();
+}
+
+bool AppCacheDatabase::DeleteGroup(int64 group_id) {
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "DELETE FROM Groups WHERE group_id = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, group_id);
+
+ return statement.Run();
+}
+
+bool AppCacheDatabase::FindCache(int64 cache_id, CacheRecord* record) {
+ DCHECK(record);
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "SELECT cache_id, group_id, online_wildcard, update_time, cache_size"
+ " FROM Caches WHERE cache_id = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, cache_id);
+
+ if (!statement.Step())
+ return false;
+
+ ReadCacheRecord(statement, record);
+ return true;
+}
+
+bool AppCacheDatabase::FindCacheForGroup(int64 group_id, CacheRecord* record) {
+ DCHECK(record);
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "SELECT cache_id, group_id, online_wildcard, update_time, cache_size"
+ " FROM Caches WHERE group_id = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, group_id);
+
+ if (!statement.Step())
+ return false;
+
+ ReadCacheRecord(statement, record);
+ return true;
+}
+
+bool AppCacheDatabase::FindCachesForOrigin(
+ const GURL& origin, std::vector<CacheRecord>* records) {
+ DCHECK(records);
+ std::vector<GroupRecord> group_records;
+ if (!FindGroupsForOrigin(origin, &group_records))
+ return false;
+
+ CacheRecord cache_record;
+ std::vector<GroupRecord>::const_iterator iter = group_records.begin();
+ while (iter != group_records.end()) {
+ if (FindCacheForGroup(iter->group_id, &cache_record))
+ records->push_back(cache_record);
+ ++iter;
+ }
+ return true;
+}
+
+bool AppCacheDatabase::InsertCache(const CacheRecord* record) {
+ if (!LazyOpen(true))
+ return false;
+
+ const char* kSql =
+ "INSERT INTO Caches (cache_id, group_id, online_wildcard,"
+ " update_time, cache_size)"
+ " VALUES(?, ?, ?, ?, ?)";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, record->cache_id);
+ statement.BindInt64(1, record->group_id);
+ statement.BindBool(2, record->online_wildcard);
+ statement.BindInt64(3, record->update_time.ToInternalValue());
+ statement.BindInt64(4, record->cache_size);
+
+ return statement.Run();
+}
+
+bool AppCacheDatabase::DeleteCache(int64 cache_id) {
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "DELETE FROM Caches WHERE cache_id = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, cache_id);
+
+ return statement.Run();
+}
+
+bool AppCacheDatabase::FindEntriesForCache(
+ int64 cache_id, std::vector<EntryRecord>* records) {
+ DCHECK(records && records->empty());
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "SELECT cache_id, url, flags, response_id, response_size FROM Entries"
+ " WHERE cache_id = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, cache_id);
+
+ while (statement.Step()) {
+ records->push_back(EntryRecord());
+ ReadEntryRecord(statement, &records->back());
+ DCHECK(records->back().cache_id == cache_id);
+ }
+
+ return statement.Succeeded();
+}
+
+bool AppCacheDatabase::FindEntriesForUrl(
+ const GURL& url, std::vector<EntryRecord>* records) {
+ DCHECK(records && records->empty());
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "SELECT cache_id, url, flags, response_id, response_size FROM Entries"
+ " WHERE url = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindString(0, url.spec());
+
+ while (statement.Step()) {
+ records->push_back(EntryRecord());
+ ReadEntryRecord(statement, &records->back());
+ DCHECK(records->back().url == url);
+ }
+
+ return statement.Succeeded();
+}
+
+bool AppCacheDatabase::FindEntry(
+ int64 cache_id, const GURL& url, EntryRecord* record) {
+ DCHECK(record);
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "SELECT cache_id, url, flags, response_id, response_size FROM Entries"
+ " WHERE cache_id = ? AND url = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, cache_id);
+ statement.BindString(1, url.spec());
+
+ if (!statement.Step())
+ return false;
+
+ ReadEntryRecord(statement, record);
+ DCHECK(record->cache_id == cache_id);
+ DCHECK(record->url == url);
+ return true;
+}
+
+bool AppCacheDatabase::InsertEntry(const EntryRecord* record) {
+ if (!LazyOpen(true))
+ return false;
+
+ const char* kSql =
+ "INSERT INTO Entries (cache_id, url, flags, response_id, response_size)"
+ " VALUES(?, ?, ?, ?, ?)";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, record->cache_id);
+ statement.BindString(1, record->url.spec());
+ statement.BindInt(2, record->flags);
+ statement.BindInt64(3, record->response_id);
+ statement.BindInt64(4, record->response_size);
+
+ return statement.Run();
+}
+
+bool AppCacheDatabase::InsertEntryRecords(
+ const std::vector<EntryRecord>& records) {
+ if (records.empty())
+ return true;
+ sql::Transaction transaction(db_.get());
+ if (!transaction.Begin())
+ return false;
+ std::vector<EntryRecord>::const_iterator iter = records.begin();
+ while (iter != records.end()) {
+ if (!InsertEntry(&(*iter)))
+ return false;
+ ++iter;
+ }
+ return transaction.Commit();
+}
+
+bool AppCacheDatabase::DeleteEntriesForCache(int64 cache_id) {
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "DELETE FROM Entries WHERE cache_id = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, cache_id);
+
+ return statement.Run();
+}
+
+bool AppCacheDatabase::AddEntryFlags(
+ const GURL& entry_url, int64 cache_id, int additional_flags) {
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "UPDATE Entries SET flags = flags | ? WHERE cache_id = ? AND url = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt(0, additional_flags);
+ statement.BindInt64(1, cache_id);
+ statement.BindString(2, entry_url.spec());
+
+ return statement.Run() && db_->GetLastChangeCount();
+}
+
+bool AppCacheDatabase::FindNamespacesForOrigin(
+ const GURL& origin,
+ std::vector<NamespaceRecord>* intercepts,
+ std::vector<NamespaceRecord>* fallbacks) {
+ DCHECK(intercepts && intercepts->empty());
+ DCHECK(fallbacks && fallbacks->empty());
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "SELECT cache_id, origin, type, namespace_url, target_url, is_pattern"
+ " FROM Namespaces WHERE origin = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindString(0, origin.spec());
+
+ ReadNamespaceRecords(&statement, intercepts, fallbacks);
+
+ return statement.Succeeded();
+}
+
+bool AppCacheDatabase::FindNamespacesForCache(
+ int64 cache_id,
+ std::vector<NamespaceRecord>* intercepts,
+ std::vector<NamespaceRecord>* fallbacks) {
+ DCHECK(intercepts && intercepts->empty());
+ DCHECK(fallbacks && fallbacks->empty());
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "SELECT cache_id, origin, type, namespace_url, target_url, is_pattern"
+ " FROM Namespaces WHERE cache_id = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, cache_id);
+
+ ReadNamespaceRecords(&statement, intercepts, fallbacks);
+
+ return statement.Succeeded();
+}
+
+bool AppCacheDatabase::InsertNamespace(
+ const NamespaceRecord* record) {
+ if (!LazyOpen(true))
+ return false;
+
+ const char* kSql =
+ "INSERT INTO Namespaces"
+ " (cache_id, origin, type, namespace_url, target_url, is_pattern)"
+ " VALUES (?, ?, ?, ?, ?, ?)";
+
+ // Note: quick and dirty storage for the 'executable' bit w/o changing
+ // schemas, we use the high bit of 'type' field.
+ int type_with_executable_bit = record->namespace_.type;
+ if (record->namespace_.is_executable) {
+ type_with_executable_bit |= 0x8000000;
+ DCHECK(CommandLine::ForCurrentProcess()->HasSwitch(
+ kEnableExecutableHandlers));
+ }
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, record->cache_id);
+ statement.BindString(1, record->origin.spec());
+ statement.BindInt(2, type_with_executable_bit);
+ statement.BindString(3, record->namespace_.namespace_url.spec());
+ statement.BindString(4, record->namespace_.target_url.spec());
+ statement.BindBool(5, record->namespace_.is_pattern);
+ return statement.Run();
+}
+
+bool AppCacheDatabase::InsertNamespaceRecords(
+ const std::vector<NamespaceRecord>& records) {
+ if (records.empty())
+ return true;
+ sql::Transaction transaction(db_.get());
+ if (!transaction.Begin())
+ return false;
+ std::vector<NamespaceRecord>::const_iterator iter = records.begin();
+ while (iter != records.end()) {
+ if (!InsertNamespace(&(*iter)))
+ return false;
+ ++iter;
+ }
+ return transaction.Commit();
+}
+
+bool AppCacheDatabase::DeleteNamespacesForCache(int64 cache_id) {
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "DELETE FROM Namespaces WHERE cache_id = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, cache_id);
+
+ return statement.Run();
+}
+
+bool AppCacheDatabase::FindOnlineWhiteListForCache(
+ int64 cache_id, std::vector<OnlineWhiteListRecord>* records) {
+ DCHECK(records && records->empty());
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "SELECT cache_id, namespace_url, is_pattern FROM OnlineWhiteLists"
+ " WHERE cache_id = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, cache_id);
+
+ while (statement.Step()) {
+ records->push_back(OnlineWhiteListRecord());
+ this->ReadOnlineWhiteListRecord(statement, &records->back());
+ DCHECK(records->back().cache_id == cache_id);
+ }
+ return statement.Succeeded();
+}
+
+bool AppCacheDatabase::InsertOnlineWhiteList(
+ const OnlineWhiteListRecord* record) {
+ if (!LazyOpen(true))
+ return false;
+
+ const char* kSql =
+ "INSERT INTO OnlineWhiteLists (cache_id, namespace_url, is_pattern)"
+ " VALUES (?, ?, ?)";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, record->cache_id);
+ statement.BindString(1, record->namespace_url.spec());
+ statement.BindBool(2, record->is_pattern);
+
+ return statement.Run();
+}
+
+bool AppCacheDatabase::InsertOnlineWhiteListRecords(
+ const std::vector<OnlineWhiteListRecord>& records) {
+ if (records.empty())
+ return true;
+ sql::Transaction transaction(db_.get());
+ if (!transaction.Begin())
+ return false;
+ std::vector<OnlineWhiteListRecord>::const_iterator iter = records.begin();
+ while (iter != records.end()) {
+ if (!InsertOnlineWhiteList(&(*iter)))
+ return false;
+ ++iter;
+ }
+ return transaction.Commit();
+}
+
+bool AppCacheDatabase::DeleteOnlineWhiteListForCache(int64 cache_id) {
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "DELETE FROM OnlineWhiteLists WHERE cache_id = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, cache_id);
+
+ return statement.Run();
+}
+
+bool AppCacheDatabase::GetDeletableResponseIds(
+ std::vector<int64>* response_ids, int64 max_rowid, int limit) {
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "SELECT response_id FROM DeletableResponseIds "
+ " WHERE rowid <= ?"
+ " LIMIT ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, max_rowid);
+ statement.BindInt64(1, limit);
+
+ while (statement.Step())
+ response_ids->push_back(statement.ColumnInt64(0));
+ return statement.Succeeded();
+}
+
+bool AppCacheDatabase::InsertDeletableResponseIds(
+ const std::vector<int64>& response_ids) {
+ const char* kSql =
+ "INSERT INTO DeletableResponseIds (response_id) VALUES (?)";
+ return RunCachedStatementWithIds(SQL_FROM_HERE, kSql, response_ids);
+}
+
+bool AppCacheDatabase::DeleteDeletableResponseIds(
+ const std::vector<int64>& response_ids) {
+ const char* kSql =
+ "DELETE FROM DeletableResponseIds WHERE response_id = ?";
+ return RunCachedStatementWithIds(SQL_FROM_HERE, kSql, response_ids);
+}
+
+bool AppCacheDatabase::RunCachedStatementWithIds(
+ const sql::StatementID& statement_id, const char* sql,
+ const std::vector<int64>& ids) {
+ DCHECK(sql);
+ if (!LazyOpen(true))
+ return false;
+
+ sql::Transaction transaction(db_.get());
+ if (!transaction.Begin())
+ return false;
+
+ sql::Statement statement(db_->GetCachedStatement(statement_id, sql));
+
+ std::vector<int64>::const_iterator iter = ids.begin();
+ while (iter != ids.end()) {
+ statement.BindInt64(0, *iter);
+ if (!statement.Run())
+ return false;
+ statement.Reset(true);
+ ++iter;
+ }
+
+ return transaction.Commit();
+}
+
+bool AppCacheDatabase::RunUniqueStatementWithInt64Result(
+ const char* sql, int64* result) {
+ DCHECK(sql);
+ sql::Statement statement(db_->GetUniqueStatement(sql));
+ if (!statement.Step()) {
+ return false;
+ }
+ *result = statement.ColumnInt64(0);
+ return true;
+}
+
+bool AppCacheDatabase::FindResponseIdsForCacheHelper(
+ int64 cache_id, std::vector<int64>* ids_vector,
+ std::set<int64>* ids_set) {
+ DCHECK(ids_vector || ids_set);
+ DCHECK(!(ids_vector && ids_set));
+ if (!LazyOpen(false))
+ return false;
+
+ const char* kSql =
+ "SELECT response_id FROM Entries WHERE cache_id = ?";
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
+
+ statement.BindInt64(0, cache_id);
+ while (statement.Step()) {
+ int64 id = statement.ColumnInt64(0);
+ if (ids_set)
+ ids_set->insert(id);
+ else
+ ids_vector->push_back(id);
+ }
+
+ return statement.Succeeded();
+}
+
+void AppCacheDatabase::ReadGroupRecord(
+ const sql::Statement& statement, GroupRecord* record) {
+ record->group_id = statement.ColumnInt64(0);
+ record->origin = GURL(statement.ColumnString(1));
+ record->manifest_url = GURL(statement.ColumnString(2));
+ record->creation_time =
+ base::Time::FromInternalValue(statement.ColumnInt64(3));
+ record->last_access_time =
+ base::Time::FromInternalValue(statement.ColumnInt64(4));
+}
+
+void AppCacheDatabase::ReadCacheRecord(
+ const sql::Statement& statement, CacheRecord* record) {
+ record->cache_id = statement.ColumnInt64(0);
+ record->group_id = statement.ColumnInt64(1);
+ record->online_wildcard = statement.ColumnBool(2);
+ record->update_time =
+ base::Time::FromInternalValue(statement.ColumnInt64(3));
+ record->cache_size = statement.ColumnInt64(4);
+}
+
+void AppCacheDatabase::ReadEntryRecord(
+ const sql::Statement& statement, EntryRecord* record) {
+ record->cache_id = statement.ColumnInt64(0);
+ record->url = GURL(statement.ColumnString(1));
+ record->flags = statement.ColumnInt(2);
+ record->response_id = statement.ColumnInt64(3);
+ record->response_size = statement.ColumnInt64(4);
+}
+
+void AppCacheDatabase::ReadNamespaceRecords(
+ sql::Statement* statement,
+ NamespaceRecordVector* intercepts,
+ NamespaceRecordVector* fallbacks) {
+ while (statement->Step()) {
+ AppCacheNamespaceType type = static_cast<AppCacheNamespaceType>(
+ statement->ColumnInt(2));
+ NamespaceRecordVector* records =
+ (type == APPCACHE_FALLBACK_NAMESPACE) ? fallbacks : intercepts;
+ records->push_back(NamespaceRecord());
+ ReadNamespaceRecord(statement, &records->back());
+ }
+}
+
+void AppCacheDatabase::ReadNamespaceRecord(
+ const sql::Statement* statement, NamespaceRecord* record) {
+ record->cache_id = statement->ColumnInt64(0);
+ record->origin = GURL(statement->ColumnString(1));
+ int type_with_executable_bit = statement->ColumnInt(2);
+ record->namespace_.namespace_url = GURL(statement->ColumnString(3));
+ record->namespace_.target_url = GURL(statement->ColumnString(4));
+ record->namespace_.is_pattern = statement->ColumnBool(5);
+
+ // Note: quick and dirty storage for the 'executable' bit w/o changing
+ // schemas, we use the high bit of 'type' field.
+ record->namespace_.type = static_cast<AppCacheNamespaceType>
+ (type_with_executable_bit & 0x7ffffff);
+ record->namespace_.is_executable =
+ (type_with_executable_bit & 0x80000000) != 0;
+ DCHECK(!record->namespace_.is_executable ||
+ CommandLine::ForCurrentProcess()->HasSwitch(kEnableExecutableHandlers));
+}
+
+void AppCacheDatabase::ReadOnlineWhiteListRecord(
+ const sql::Statement& statement, OnlineWhiteListRecord* record) {
+ record->cache_id = statement.ColumnInt64(0);
+ record->namespace_url = GURL(statement.ColumnString(1));
+ record->is_pattern = statement.ColumnBool(2);
+}
+
+bool AppCacheDatabase::LazyOpen(bool create_if_needed) {
+ if (db_)
+ return true;
+
+ // If we tried and failed once, don't try again in the same session
+ // to avoid creating an incoherent mess on disk.
+ if (is_disabled_)
+ return false;
+
+ // Avoid creating a database at all if we can.
+ bool use_in_memory_db = db_file_path_.empty();
+ if (!create_if_needed &&
+ (use_in_memory_db || !base::PathExists(db_file_path_))) {
+ return false;
+ }
+
+ db_.reset(new sql::Connection);
+ meta_table_.reset(new sql::MetaTable);
+
+ db_->set_histogram_tag("AppCache");
+
+ bool opened = false;
+ if (use_in_memory_db) {
+ opened = db_->OpenInMemory();
+ } else if (!base::CreateDirectory(db_file_path_.DirName())) {
+ LOG(ERROR) << "Failed to create appcache directory.";
+ } else {
+ opened = db_->Open(db_file_path_);
+ if (opened)
+ db_->Preload();
+ }
+
+ if (!opened || !db_->QuickIntegrityCheck() || !EnsureDatabaseVersion()) {
+ LOG(ERROR) << "Failed to open the appcache database.";
+ AppCacheHistograms::CountInitResult(
+ AppCacheHistograms::SQL_DATABASE_ERROR);
+
+ // We're unable to open the database. This is a fatal error
+ // which we can't recover from. We try to handle it by deleting
+ // the existing appcache data and starting with a clean slate in
+ // this browser session.
+ if (!use_in_memory_db && DeleteExistingAndCreateNewDatabase())
+ return true;
+
+ Disable();
+ return false;
+ }
+
+ AppCacheHistograms::CountInitResult(AppCacheHistograms::INIT_OK);
+ was_corruption_detected_ = false;
+ db_->set_error_callback(
+ base::Bind(&AppCacheDatabase::OnDatabaseError, base::Unretained(this)));
+ return true;
+}
+
+bool AppCacheDatabase::EnsureDatabaseVersion() {
+ if (!sql::MetaTable::DoesTableExist(db_.get()))
+ return CreateSchema();
+
+ if (!meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion))
+ return false;
+
+ if (meta_table_->GetCompatibleVersionNumber() > kCurrentVersion) {
+ LOG(WARNING) << "AppCache database is too new.";
+ return false;
+ }
+
+ std::string stored_flags;
+ meta_table_->GetValue(kExperimentFlagsKey, &stored_flags);
+ if (stored_flags != GetActiveExperimentFlags())
+ return false;
+
+ if (meta_table_->GetVersionNumber() < kCurrentVersion)
+ return UpgradeSchema();
+
+#ifndef NDEBUG
+ DCHECK(sql::MetaTable::DoesTableExist(db_.get()));
+ for (int i = 0; i < kTableCount; ++i) {
+ DCHECK(db_->DoesTableExist(kTables[i].table_name));
+ }
+ for (int i = 0; i < kIndexCount; ++i) {
+ DCHECK(db_->DoesIndexExist(kIndexes[i].index_name));
+ }
+#endif
+
+ return true;
+}
+
+bool AppCacheDatabase::CreateSchema() {
+ sql::Transaction transaction(db_.get());
+ if (!transaction.Begin())
+ return false;
+
+ if (!meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion))
+ return false;
+
+ if (!meta_table_->SetValue(kExperimentFlagsKey,
+ GetActiveExperimentFlags())) {
+ return false;
+ }
+
+ for (int i = 0; i < kTableCount; ++i) {
+ if (!CreateTable(db_.get(), kTables[i]))
+ return false;
+ }
+
+ for (int i = 0; i < kIndexCount; ++i) {
+ if (!CreateIndex(db_.get(), kIndexes[i]))
+ return false;
+ }
+
+ return transaction.Commit();
+}
+
+bool AppCacheDatabase::UpgradeSchema() {
+#if defined(APPCACHE_USE_SIMPLE_CACHE)
+ return DeleteExistingAndCreateNewDatabase();
+#else
+ if (meta_table_->GetVersionNumber() == 3) {
+ // version 3 was pre 12/17/2011
+ DCHECK_EQ(strcmp(kNamespacesTable, kTables[3].table_name), 0);
+ DCHECK_EQ(strcmp(kNamespacesTable, kIndexes[6].table_name), 0);
+ DCHECK_EQ(strcmp(kNamespacesTable, kIndexes[7].table_name), 0);
+ DCHECK_EQ(strcmp(kNamespacesTable, kIndexes[8].table_name), 0);
+
+ const TableInfo kNamespaceTable_v4 = {
+ kNamespacesTable,
+ "(cache_id INTEGER,"
+ " origin TEXT," // intentionally not normalized
+ " type INTEGER,"
+ " namespace_url TEXT,"
+ " target_url TEXT)"
+ };
+
+ // Migrate from the old FallbackNameSpaces to the newer Namespaces table,
+ // but without the is_pattern column added in v5.
+ sql::Transaction transaction(db_.get());
+ if (!transaction.Begin() ||
+ !CreateTable(db_.get(), kNamespaceTable_v4)) {
+ return false;
+ }
+
+ // Move data from the old table to the new table, setting the
+ // 'type' for all current records to the value for
+ // APPCACHE_FALLBACK_NAMESPACE.
+ DCHECK_EQ(0, static_cast<int>(APPCACHE_FALLBACK_NAMESPACE));
+ if (!db_->Execute(
+ "INSERT INTO Namespaces"
+ " SELECT cache_id, origin, 0, namespace_url, fallback_entry_url"
+ " FROM FallbackNameSpaces")) {
+ return false;
+ }
+
+ // Drop the old table, indexes on that table are also removed by this.
+ if (!db_->Execute("DROP TABLE FallbackNameSpaces"))
+ return false;
+
+ // Create new indexes.
+ if (!CreateIndex(db_.get(), kIndexes[6]) ||
+ !CreateIndex(db_.get(), kIndexes[7]) ||
+ !CreateIndex(db_.get(), kIndexes[8])) {
+ return false;
+ }
+
+ meta_table_->SetVersionNumber(4);
+ meta_table_->SetCompatibleVersionNumber(4);
+ if (!transaction.Commit())
+ return false;
+ }
+
+ if (meta_table_->GetVersionNumber() == 4) {
+ // version 4 pre 3/30/2013
+ // Add the is_pattern column to the Namespaces and OnlineWhitelists tables.
+ DCHECK_EQ(strcmp(kNamespacesTable, "Namespaces"), 0);
+ sql::Transaction transaction(db_.get());
+ if (!transaction.Begin())
+ return false;
+ if (!db_->Execute(
+ "ALTER TABLE Namespaces ADD COLUMN"
+ " is_pattern INTEGER CHECK(is_pattern IN (0, 1))")) {
+ return false;
+ }
+ if (!db_->Execute(
+ "ALTER TABLE OnlineWhitelists ADD COLUMN"
+ " is_pattern INTEGER CHECK(is_pattern IN (0, 1))")) {
+ return false;
+ }
+ meta_table_->SetVersionNumber(5);
+ meta_table_->SetCompatibleVersionNumber(5);
+ return transaction.Commit();
+ }
+
+ // If there is no upgrade path for the version on disk to the current
+ // version, nuke everything and start over.
+ return DeleteExistingAndCreateNewDatabase();
+#endif
+}
+
+void AppCacheDatabase::ResetConnectionAndTables() {
+ meta_table_.reset();
+ db_.reset();
+}
+
+bool AppCacheDatabase::DeleteExistingAndCreateNewDatabase() {
+ DCHECK(!db_file_path_.empty());
+ DCHECK(base::PathExists(db_file_path_));
+ VLOG(1) << "Deleting existing appcache data and starting over.";
+
+ ResetConnectionAndTables();
+
+ // This also deletes the disk cache data.
+ base::FilePath directory = db_file_path_.DirName();
+ if (!base::DeleteFile(directory, true))
+ return false;
+
+ // Make sure the steps above actually deleted things.
+ if (base::PathExists(directory))
+ return false;
+
+ if (!base::CreateDirectory(directory))
+ return false;
+
+ // So we can't go recursive.
+ if (is_recreating_)
+ return false;
+
+ base::AutoReset<bool> auto_reset(&is_recreating_, true);
+ return LazyOpen(true);
+}
+
+void AppCacheDatabase::OnDatabaseError(int err, sql::Statement* stmt) {
+ was_corruption_detected_ |= sql::IsErrorCatastrophic(err);
+ if (!db_->ShouldIgnoreSqliteError(err))
+ DLOG(ERROR) << db_->GetErrorMessage();
+ // TODO: Maybe use non-catostrophic errors to trigger a full integrity check?
+}
+
+} // namespace content
diff --git a/content/browser/appcache/appcache_database.h b/content/browser/appcache/appcache_database.h
new file mode 100644
index 0000000..2fe5e12
--- /dev/null
+++ b/content/browser/appcache/appcache_database.h
@@ -0,0 +1,254 @@
+// Copyright (c) 2012 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.
+
+#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_DATABASE_H_
+#define CONTENT_BROWSER_APPCACHE_APPCACHE_DATABASE_H_
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
+#include "content/common/appcache_interfaces.h"
+#include "content/common/content_export.h"
+#include "url/gurl.h"
+
+namespace sql {
+class Connection;
+class MetaTable;
+class Statement;
+class StatementID;
+}
+
+namespace content {
+FORWARD_DECLARE_TEST(AppCacheDatabaseTest, CacheRecords);
+FORWARD_DECLARE_TEST(AppCacheDatabaseTest, EntryRecords);
+FORWARD_DECLARE_TEST(AppCacheDatabaseTest, QuickIntegrityCheck);
+FORWARD_DECLARE_TEST(AppCacheDatabaseTest, NamespaceRecords);
+FORWARD_DECLARE_TEST(AppCacheDatabaseTest, GroupRecords);
+FORWARD_DECLARE_TEST(AppCacheDatabaseTest, LazyOpen);
+FORWARD_DECLARE_TEST(AppCacheDatabaseTest, ExperimentalFlags);
+FORWARD_DECLARE_TEST(AppCacheDatabaseTest, OnlineWhiteListRecords);
+FORWARD_DECLARE_TEST(AppCacheDatabaseTest, ReCreate);
+FORWARD_DECLARE_TEST(AppCacheDatabaseTest, DeletableResponseIds);
+FORWARD_DECLARE_TEST(AppCacheDatabaseTest, OriginUsage);
+FORWARD_DECLARE_TEST(AppCacheDatabaseTest, UpgradeSchema3to5);
+FORWARD_DECLARE_TEST(AppCacheDatabaseTest, UpgradeSchema4to5);
+FORWARD_DECLARE_TEST(AppCacheDatabaseTest, WasCorrutionDetected);
+class AppCacheDatabaseTest;
+class AppCacheStorageImplTest;
+}
+
+namespace content {
+
+class CONTENT_EXPORT AppCacheDatabase {
+ public:
+ struct CONTENT_EXPORT GroupRecord {
+ GroupRecord();
+ ~GroupRecord();
+
+ int64 group_id;
+ GURL origin;
+ GURL manifest_url;
+ base::Time creation_time;
+ base::Time last_access_time;
+ };
+
+ struct CONTENT_EXPORT CacheRecord {
+ CacheRecord()
+ : cache_id(0), group_id(0), online_wildcard(false), cache_size(0) {}
+
+ int64 cache_id;
+ int64 group_id;
+ bool online_wildcard;
+ base::Time update_time;
+ int64 cache_size; // the sum of all response sizes in this cache
+ };
+
+ struct EntryRecord {
+ EntryRecord() : cache_id(0), flags(0), response_id(0), response_size(0) {}
+
+ int64 cache_id;
+ GURL url;
+ int flags;
+ int64 response_id;
+ int64 response_size;
+ };
+
+ struct CONTENT_EXPORT NamespaceRecord {
+ NamespaceRecord();
+ ~NamespaceRecord();
+
+ int64 cache_id;
+ GURL origin;
+ AppCacheNamespace namespace_;
+ };
+
+ typedef std::vector<NamespaceRecord> NamespaceRecordVector;
+
+ struct OnlineWhiteListRecord {
+ OnlineWhiteListRecord() : cache_id(0), is_pattern(false) {}
+
+ int64 cache_id;
+ GURL namespace_url;
+ bool is_pattern;
+ };
+
+ explicit AppCacheDatabase(const base::FilePath& path);
+ ~AppCacheDatabase();
+
+ void Disable();
+ bool is_disabled() const { return is_disabled_; }
+ bool was_corruption_detected() const { return was_corruption_detected_; }
+
+ int64 GetOriginUsage(const GURL& origin);
+ bool GetAllOriginUsage(std::map<GURL, int64>* usage_map);
+
+ bool FindOriginsWithGroups(std::set<GURL>* origins);
+ bool FindLastStorageIds(
+ int64* last_group_id, int64* last_cache_id, int64* last_response_id,
+ int64* last_deletable_response_rowid);
+
+ bool FindGroup(int64 group_id, GroupRecord* record);
+ bool FindGroupForManifestUrl(const GURL& manifest_url, GroupRecord* record);
+ bool FindGroupsForOrigin(
+ const GURL& origin, std::vector<GroupRecord>* records);
+ bool FindGroupForCache(int64 cache_id, GroupRecord* record);
+ bool UpdateGroupLastAccessTime(
+ int64 group_id, base::Time last_access_time);
+ bool InsertGroup(const GroupRecord* record);
+ bool DeleteGroup(int64 group_id);
+
+ bool FindCache(int64 cache_id, CacheRecord* record);
+ bool FindCacheForGroup(int64 group_id, CacheRecord* record);
+ bool FindCachesForOrigin(
+ const GURL& origin, std::vector<CacheRecord>* records);
+ bool InsertCache(const CacheRecord* record);
+ bool DeleteCache(int64 cache_id);
+
+ bool FindEntriesForCache(
+ int64 cache_id, std::vector<EntryRecord>* records);
+ bool FindEntriesForUrl(
+ const GURL& url, std::vector<EntryRecord>* records);
+ bool FindEntry(int64 cache_id, const GURL& url, EntryRecord* record);
+ bool InsertEntry(const EntryRecord* record);
+ bool InsertEntryRecords(
+ const std::vector<EntryRecord>& records);
+ bool DeleteEntriesForCache(int64 cache_id);
+ bool AddEntryFlags(const GURL& entry_url, int64 cache_id,
+ int additional_flags);
+ bool FindResponseIdsForCacheAsVector(
+ int64 cache_id, std::vector<int64>* response_ids) {
+ return FindResponseIdsForCacheHelper(cache_id, response_ids, NULL);
+ }
+ bool FindResponseIdsForCacheAsSet(
+ int64 cache_id, std::set<int64>* response_ids) {
+ return FindResponseIdsForCacheHelper(cache_id, NULL, response_ids);
+ }
+
+ bool FindNamespacesForOrigin(
+ const GURL& origin,
+ NamespaceRecordVector* intercepts,
+ NamespaceRecordVector* fallbacks);
+ bool FindNamespacesForCache(
+ int64 cache_id,
+ NamespaceRecordVector* intercepts,
+ std::vector<NamespaceRecord>* fallbacks);
+ bool InsertNamespaceRecords(
+ const NamespaceRecordVector& records);
+ bool InsertNamespace(const NamespaceRecord* record);
+ bool DeleteNamespacesForCache(int64 cache_id);
+
+ bool FindOnlineWhiteListForCache(
+ int64 cache_id, std::vector<OnlineWhiteListRecord>* records);
+ bool InsertOnlineWhiteList(const OnlineWhiteListRecord* record);
+ bool InsertOnlineWhiteListRecords(
+ const std::vector<OnlineWhiteListRecord>& records);
+ bool DeleteOnlineWhiteListForCache(int64 cache_id);
+
+ bool GetDeletableResponseIds(std::vector<int64>* response_ids,
+ int64 max_rowid, int limit);
+ bool InsertDeletableResponseIds(const std::vector<int64>& response_ids);
+ bool DeleteDeletableResponseIds(const std::vector<int64>& response_ids);
+
+ // So our callers can wrap operations in transactions.
+ sql::Connection* db_connection() {
+ LazyOpen(true);
+ return db_.get();
+ }
+
+ private:
+ bool RunCachedStatementWithIds(
+ const sql::StatementID& statement_id, const char* sql,
+ const std::vector<int64>& ids);
+ bool RunUniqueStatementWithInt64Result(const char* sql, int64* result);
+
+ bool FindResponseIdsForCacheHelper(
+ int64 cache_id, std::vector<int64>* ids_vector,
+ std::set<int64>* ids_set);
+
+ // Record retrieval helpers
+ void ReadGroupRecord(const sql::Statement& statement, GroupRecord* record);
+ void ReadCacheRecord(const sql::Statement& statement, CacheRecord* record);
+ void ReadEntryRecord(const sql::Statement& statement, EntryRecord* record);
+ void ReadNamespaceRecords(
+ sql::Statement* statement,
+ NamespaceRecordVector* intercepts,
+ NamespaceRecordVector* fallbacks);
+ void ReadNamespaceRecord(
+ const sql::Statement* statement, NamespaceRecord* record);
+ void ReadOnlineWhiteListRecord(
+ const sql::Statement& statement, OnlineWhiteListRecord* record);
+
+ // Database creation
+ bool LazyOpen(bool create_if_needed);
+ bool EnsureDatabaseVersion();
+ bool CreateSchema();
+ bool UpgradeSchema();
+
+ void ResetConnectionAndTables();
+
+ // Deletes the existing database file and the entire directory containing
+ // the database file including the disk cache in which response headers
+ // and bodies are stored, and then creates a new database file.
+ bool DeleteExistingAndCreateNewDatabase();
+
+ void OnDatabaseError(int err, sql::Statement* stmt);
+
+ base::FilePath db_file_path_;
+ scoped_ptr<sql::Connection> db_;
+ scoped_ptr<sql::MetaTable> meta_table_;
+ bool is_disabled_;
+ bool is_recreating_;
+ bool was_corruption_detected_;
+
+ friend class content::AppCacheDatabaseTest;
+ friend class content::AppCacheStorageImplTest;
+
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheDatabaseTest, CacheRecords);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheDatabaseTest, EntryRecords);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheDatabaseTest, QuickIntegrityCheck);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheDatabaseTest, NamespaceRecords);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheDatabaseTest, GroupRecords);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheDatabaseTest, LazyOpen);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheDatabaseTest, ExperimentalFlags);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheDatabaseTest,
+ OnlineWhiteListRecords);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheDatabaseTest, ReCreate);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheDatabaseTest, DeletableResponseIds);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheDatabaseTest, OriginUsage);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheDatabaseTest, UpgradeSchema3to5);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheDatabaseTest, UpgradeSchema4to5);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheDatabaseTest, WasCorrutionDetected);
+
+ DISALLOW_COPY_AND_ASSIGN(AppCacheDatabase);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_APPCACHE_APPCACHE_DATABASE_H_
diff --git a/content/browser/appcache/appcache_database_unittest.cc b/content/browser/appcache/appcache_database_unittest.cc
index 92fa6a5..44a6c6a 100644
--- a/content/browser/appcache/appcache_database_unittest.cc
+++ b/content/browser/appcache/appcache_database_unittest.cc
@@ -6,6 +6,8 @@
#include "base/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/strings/stringprintf.h"
+#include "content/browser/appcache/appcache_database.h"
+#include "content/browser/appcache/appcache_entry.h"
#include "sql/connection.h"
#include "sql/meta_table.h"
#include "sql/statement.h"
@@ -14,14 +16,6 @@
#include "sql/transaction.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/sqlite/sqlite3.h"
-#include "webkit/browser/appcache/appcache_database.h"
-#include "webkit/browser/appcache/appcache_entry.h"
-
-using appcache::AppCacheDatabase;
-using appcache::AppCacheEntry;
-using appcache::APPCACHE_FALLBACK_NAMESPACE;
-using appcache::APPCACHE_INTERCEPT_NAMESPACE;
-using appcache::APPCACHE_NETWORK_NAMESPACE;
namespace {
diff --git a/content/browser/appcache/appcache_disk_cache.cc b/content/browser/appcache/appcache_disk_cache.cc
new file mode 100644
index 0000000..50995c7
--- /dev/null
+++ b/content/browser/appcache/appcache_disk_cache.cc
@@ -0,0 +1,351 @@
+// Copyright (c) 2012 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 "content/browser/appcache/appcache_disk_cache.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/base/cache_type.h"
+#include "net/base/net_errors.h"
+
+namespace content {
+
+// A callback shim that provides storage for the 'backend_ptr' value
+// and will delete a resulting ptr if completion occurs after its
+// been canceled.
+class AppCacheDiskCache::CreateBackendCallbackShim
+ : public base::RefCounted<CreateBackendCallbackShim> {
+ public:
+ explicit CreateBackendCallbackShim(AppCacheDiskCache* object)
+ : appcache_diskcache_(object) {
+ }
+
+ void Cancel() {
+ appcache_diskcache_ = NULL;
+ }
+
+ void Callback(int rv) {
+ if (appcache_diskcache_)
+ appcache_diskcache_->OnCreateBackendComplete(rv);
+ }
+
+ scoped_ptr<disk_cache::Backend> backend_ptr_; // Accessed directly.
+
+ private:
+ friend class base::RefCounted<CreateBackendCallbackShim>;
+
+ ~CreateBackendCallbackShim() {
+ }
+
+ AppCacheDiskCache* appcache_diskcache_; // Unowned pointer.
+};
+
+// An implementation of AppCacheDiskCacheInterface::Entry that's a thin
+// wrapper around disk_cache::Entry.
+class AppCacheDiskCache::EntryImpl : public Entry {
+ public:
+ EntryImpl(disk_cache::Entry* disk_cache_entry,
+ AppCacheDiskCache* owner)
+ : disk_cache_entry_(disk_cache_entry), owner_(owner) {
+ DCHECK(disk_cache_entry);
+ DCHECK(owner);
+ owner_->AddOpenEntry(this);
+ }
+
+ // Entry implementation.
+ virtual int Read(int index, int64 offset, net::IOBuffer* buf, int buf_len,
+ const net::CompletionCallback& callback) OVERRIDE {
+ if (offset < 0 || offset > kint32max)
+ return net::ERR_INVALID_ARGUMENT;
+ if (!disk_cache_entry_)
+ return net::ERR_ABORTED;
+ return disk_cache_entry_->ReadData(
+ index, static_cast<int>(offset), buf, buf_len, callback);
+ }
+
+ virtual int Write(int index, int64 offset, net::IOBuffer* buf, int buf_len,
+ const net::CompletionCallback& callback) OVERRIDE {
+ if (offset < 0 || offset > kint32max)
+ return net::ERR_INVALID_ARGUMENT;
+ if (!disk_cache_entry_)
+ return net::ERR_ABORTED;
+ const bool kTruncate = true;
+ return disk_cache_entry_->WriteData(
+ index, static_cast<int>(offset), buf, buf_len, callback, kTruncate);
+ }
+
+ virtual int64 GetSize(int index) OVERRIDE {
+ return disk_cache_entry_ ? disk_cache_entry_->GetDataSize(index) : 0L;
+ }
+
+ virtual void Close() OVERRIDE {
+ if (disk_cache_entry_)
+ disk_cache_entry_->Close();
+ delete this;
+ }
+
+ void Abandon() {
+ owner_ = NULL;
+ disk_cache_entry_->Close();
+ disk_cache_entry_ = NULL;
+ }
+
+ private:
+ virtual ~EntryImpl() {
+ if (owner_)
+ owner_->RemoveOpenEntry(this);
+ }
+
+ disk_cache::Entry* disk_cache_entry_;
+ AppCacheDiskCache* owner_;
+};
+
+// Separate object to hold state for each Create, Delete, or Doom call
+// while the call is in-flight and to produce an EntryImpl upon completion.
+class AppCacheDiskCache::ActiveCall {
+ public:
+ explicit ActiveCall(AppCacheDiskCache* owner)
+ : entry_(NULL),
+ owner_(owner),
+ entry_ptr_(NULL) {
+ }
+
+ int CreateEntry(int64 key, Entry** entry,
+ const net::CompletionCallback& callback) {
+ int rv = owner_->disk_cache()->CreateEntry(
+ base::Int64ToString(key), &entry_ptr_,
+ base::Bind(&ActiveCall::OnAsyncCompletion, base::Unretained(this)));
+ return HandleImmediateReturnValue(rv, entry, callback);
+ }
+
+ int OpenEntry(int64 key, Entry** entry,
+ const net::CompletionCallback& callback) {
+ int rv = owner_->disk_cache()->OpenEntry(
+ base::Int64ToString(key), &entry_ptr_,
+ base::Bind(&ActiveCall::OnAsyncCompletion, base::Unretained(this)));
+ return HandleImmediateReturnValue(rv, entry, callback);
+ }
+
+ int DoomEntry(int64 key, const net::CompletionCallback& callback) {
+ int rv = owner_->disk_cache()->DoomEntry(
+ base::Int64ToString(key),
+ base::Bind(&ActiveCall::OnAsyncCompletion, base::Unretained(this)));
+ return HandleImmediateReturnValue(rv, NULL, callback);
+ }
+
+ private:
+ int HandleImmediateReturnValue(int rv, Entry** entry,
+ const net::CompletionCallback& callback) {
+ if (rv == net::ERR_IO_PENDING) {
+ // OnAsyncCompletion will be called later.
+ callback_ = callback;
+ entry_ = entry;
+ owner_->AddActiveCall(this);
+ return net::ERR_IO_PENDING;
+ }
+ if (rv == net::OK && entry)
+ *entry = new EntryImpl(entry_ptr_, owner_);
+ delete this;
+ return rv;
+ }
+
+ void OnAsyncCompletion(int rv) {
+ owner_->RemoveActiveCall(this);
+ if (rv == net::OK && entry_)
+ *entry_ = new EntryImpl(entry_ptr_, owner_);
+ callback_.Run(rv);
+ callback_.Reset();
+ delete this;
+ }
+
+ Entry** entry_;
+ net::CompletionCallback callback_;
+ AppCacheDiskCache* owner_;
+ disk_cache::Entry* entry_ptr_;
+};
+
+AppCacheDiskCache::AppCacheDiskCache()
+ : is_disabled_(false) {
+}
+
+AppCacheDiskCache::~AppCacheDiskCache() {
+ Disable();
+}
+
+int AppCacheDiskCache::InitWithDiskBackend(
+ const base::FilePath& disk_cache_directory, int disk_cache_size, bool force,
+ base::MessageLoopProxy* cache_thread,
+ const net::CompletionCallback& callback) {
+ return Init(net::APP_CACHE, disk_cache_directory,
+ disk_cache_size, force, cache_thread, callback);
+}
+
+int AppCacheDiskCache::InitWithMemBackend(
+ int mem_cache_size, const net::CompletionCallback& callback) {
+ return Init(net::MEMORY_CACHE, base::FilePath(), mem_cache_size, false, NULL,
+ callback);
+}
+
+void AppCacheDiskCache::Disable() {
+ if (is_disabled_)
+ return;
+
+ is_disabled_ = true;
+
+ if (create_backend_callback_.get()) {
+ create_backend_callback_->Cancel();
+ create_backend_callback_ = NULL;
+ OnCreateBackendComplete(net::ERR_ABORTED);
+ }
+
+ // We need to close open file handles in order to reinitalize the
+ // appcache system on the fly. File handles held in both entries and in
+ // the main disk_cache::Backend class need to be released.
+ for (OpenEntries::const_iterator iter = open_entries_.begin();
+ iter != open_entries_.end(); ++iter) {
+ (*iter)->Abandon();
+ }
+ open_entries_.clear();
+ disk_cache_.reset();
+ STLDeleteElements(&active_calls_);
+}
+
+int AppCacheDiskCache::CreateEntry(int64 key, Entry** entry,
+ const net::CompletionCallback& callback) {
+ DCHECK(entry);
+ DCHECK(!callback.is_null());
+ if (is_disabled_)
+ return net::ERR_ABORTED;
+
+ if (is_initializing()) {
+ pending_calls_.push_back(PendingCall(CREATE, key, entry, callback));
+ return net::ERR_IO_PENDING;
+ }
+
+ if (!disk_cache_)
+ return net::ERR_FAILED;
+
+ return (new ActiveCall(this))->CreateEntry(key, entry, callback);
+}
+
+int AppCacheDiskCache::OpenEntry(int64 key, Entry** entry,
+ const net::CompletionCallback& callback) {
+ DCHECK(entry);
+ DCHECK(!callback.is_null());
+ if (is_disabled_)
+ return net::ERR_ABORTED;
+
+ if (is_initializing()) {
+ pending_calls_.push_back(PendingCall(OPEN, key, entry, callback));
+ return net::ERR_IO_PENDING;
+ }
+
+ if (!disk_cache_)
+ return net::ERR_FAILED;
+
+ return (new ActiveCall(this))->OpenEntry(key, entry, callback);
+}
+
+int AppCacheDiskCache::DoomEntry(int64 key,
+ const net::CompletionCallback& callback) {
+ DCHECK(!callback.is_null());
+ if (is_disabled_)
+ return net::ERR_ABORTED;
+
+ if (is_initializing()) {
+ pending_calls_.push_back(PendingCall(DOOM, key, NULL, callback));
+ return net::ERR_IO_PENDING;
+ }
+
+ if (!disk_cache_)
+ return net::ERR_FAILED;
+
+ return (new ActiveCall(this))->DoomEntry(key, callback);
+}
+
+AppCacheDiskCache::PendingCall::PendingCall()
+ : call_type(CREATE),
+ key(0),
+ entry(NULL) {
+}
+
+AppCacheDiskCache::PendingCall::PendingCall(PendingCallType call_type,
+ int64 key,
+ Entry** entry,
+ const net::CompletionCallback& callback)
+ : call_type(call_type),
+ key(key),
+ entry(entry),
+ callback(callback) {
+}
+
+AppCacheDiskCache::PendingCall::~PendingCall() {}
+
+int AppCacheDiskCache::Init(net::CacheType cache_type,
+ const base::FilePath& cache_directory,
+ int cache_size, bool force,
+ base::MessageLoopProxy* cache_thread,
+ const net::CompletionCallback& callback) {
+ DCHECK(!is_initializing() && !disk_cache_.get());
+ is_disabled_ = false;
+ create_backend_callback_ = new CreateBackendCallbackShim(this);
+
+#if defined(APPCACHE_USE_SIMPLE_CACHE)
+ const net::BackendType backend_type = net::CACHE_BACKEND_SIMPLE;
+#else
+ const net::BackendType backend_type = net::CACHE_BACKEND_DEFAULT;
+#endif
+ int rv = disk_cache::CreateCacheBackend(
+ cache_type, backend_type, cache_directory, cache_size,
+ force, cache_thread, NULL, &(create_backend_callback_->backend_ptr_),
+ base::Bind(&CreateBackendCallbackShim::Callback,
+ create_backend_callback_));
+ if (rv == net::ERR_IO_PENDING)
+ init_callback_ = callback;
+ else
+ OnCreateBackendComplete(rv);
+ return rv;
+}
+
+void AppCacheDiskCache::OnCreateBackendComplete(int rv) {
+ if (rv == net::OK) {
+ disk_cache_ = create_backend_callback_->backend_ptr_.Pass();
+ }
+ create_backend_callback_ = NULL;
+
+ // Invoke our clients callback function.
+ if (!init_callback_.is_null()) {
+ init_callback_.Run(rv);
+ init_callback_.Reset();
+ }
+
+ // Service pending calls that were queued up while we were initializing.
+ for (PendingCalls::const_iterator iter = pending_calls_.begin();
+ iter < pending_calls_.end(); ++iter) {
+ int rv = net::ERR_FAILED;
+ switch (iter->call_type) {
+ case CREATE:
+ rv = CreateEntry(iter->key, iter->entry, iter->callback);
+ break;
+ case OPEN:
+ rv = OpenEntry(iter->key, iter->entry, iter->callback);
+ break;
+ case DOOM:
+ rv = DoomEntry(iter->key, iter->callback);
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ if (rv != net::ERR_IO_PENDING)
+ iter->callback.Run(rv);
+ }
+ pending_calls_.clear();
+}
+
+} // namespace content
diff --git a/content/browser/appcache/appcache_disk_cache.h b/content/browser/appcache/appcache_disk_cache.h
new file mode 100644
index 0000000..3d51889
--- /dev/null
+++ b/content/browser/appcache/appcache_disk_cache.h
@@ -0,0 +1,104 @@
+// Copyright (c) 2012 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.
+
+#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_DISK_CACHE_H_
+#define CONTENT_BROWSER_APPCACHE_APPCACHE_DISK_CACHE_H_
+
+#include <set>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/appcache/appcache_response.h"
+#include "content/common/content_export.h"
+#include "net/disk_cache/disk_cache.h"
+
+namespace content {
+
+// An implementation of AppCacheDiskCacheInterface that
+// uses net::DiskCache as the backing store.
+class CONTENT_EXPORT AppCacheDiskCache
+ : public AppCacheDiskCacheInterface {
+ public:
+ AppCacheDiskCache();
+ virtual ~AppCacheDiskCache();
+
+ // Initializes the object to use disk backed storage.
+ int InitWithDiskBackend(const base::FilePath& disk_cache_directory,
+ int disk_cache_size, bool force,
+ base::MessageLoopProxy* cache_thread,
+ const net::CompletionCallback& callback);
+
+ // Initializes the object to use memory only storage.
+ // This is used for Chrome's incognito browsing.
+ int InitWithMemBackend(int disk_cache_size,
+ const net::CompletionCallback& callback);
+
+ void Disable();
+ bool is_disabled() const { return is_disabled_; }
+
+ virtual int CreateEntry(int64 key, Entry** entry,
+ const net::CompletionCallback& callback) OVERRIDE;
+ virtual int OpenEntry(int64 key, Entry** entry,
+ const net::CompletionCallback& callback) OVERRIDE;
+ virtual int DoomEntry(int64 key,
+ const net::CompletionCallback& callback) OVERRIDE;
+
+ private:
+ class CreateBackendCallbackShim;
+ class EntryImpl;
+
+ // PendingCalls allow CreateEntry, OpenEntry, and DoomEntry to be called
+ // immediately after construction, without waiting for the
+ // underlying disk_cache::Backend to be fully constructed. Early
+ // calls are queued up and serviced once the disk_cache::Backend is
+ // really ready to go.
+ enum PendingCallType {
+ CREATE,
+ OPEN,
+ DOOM
+ };
+ struct PendingCall {
+ PendingCallType call_type;
+ int64 key;
+ Entry** entry;
+ net::CompletionCallback callback;
+
+ PendingCall();
+
+ PendingCall(PendingCallType call_type, int64 key,
+ Entry** entry, const net::CompletionCallback& callback);
+
+ ~PendingCall();
+ };
+ typedef std::vector<PendingCall> PendingCalls;
+
+ class ActiveCall;
+ typedef std::set<ActiveCall*> ActiveCalls;
+ typedef std::set<EntryImpl*> OpenEntries;
+
+ bool is_initializing() const {
+ return create_backend_callback_.get() != NULL;
+ }
+ disk_cache::Backend* disk_cache() { return disk_cache_.get(); }
+ int Init(net::CacheType cache_type, const base::FilePath& directory,
+ int cache_size, bool force, base::MessageLoopProxy* cache_thread,
+ const net::CompletionCallback& callback);
+ void OnCreateBackendComplete(int rv);
+ void AddActiveCall(ActiveCall* call) { active_calls_.insert(call); }
+ void RemoveActiveCall(ActiveCall* call) { active_calls_.erase(call); }
+ void AddOpenEntry(EntryImpl* entry) { open_entries_.insert(entry); }
+ void RemoveOpenEntry(EntryImpl* entry) { open_entries_.erase(entry); }
+
+ bool is_disabled_;
+ net::CompletionCallback init_callback_;
+ scoped_refptr<CreateBackendCallbackShim> create_backend_callback_;
+ PendingCalls pending_calls_;
+ ActiveCalls active_calls_;
+ OpenEntries open_entries_;
+ scoped_ptr<disk_cache::Backend> disk_cache_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_APPCACHE_APPCACHE_DISK_CACHE_H_
diff --git a/content/browser/appcache/appcache_disk_cache_unittest.cc b/content/browser/appcache/appcache_disk_cache_unittest.cc
index 5e1e083..b7e1407 100644
--- a/content/browser/appcache/appcache_disk_cache_unittest.cc
+++ b/content/browser/appcache/appcache_disk_cache_unittest.cc
@@ -7,12 +7,10 @@
#include "base/files/scoped_temp_dir.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/run_loop.h"
+#include "content/browser/appcache/appcache_disk_cache.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/appcache/appcache_disk_cache.h"
-
-using appcache::AppCacheDiskCache;
namespace content {
diff --git a/content/browser/appcache/appcache_dispatcher_host.cc b/content/browser/appcache/appcache_dispatcher_host.cc
index 64e46b1..eac1eff 100644
--- a/content/browser/appcache/appcache_dispatcher_host.cc
+++ b/content/browser/appcache/appcache_dispatcher_host.cc
@@ -103,7 +103,7 @@ void AppCacheDispatcherHost::OnSelectCache(
BadMessageReceived();
}
} else {
- frontend_proxy_.OnCacheSelected(host_id, appcache::AppCacheInfo());
+ frontend_proxy_.OnCacheSelected(host_id, AppCacheInfo());
}
}
@@ -115,7 +115,7 @@ void AppCacheDispatcherHost::OnSelectCacheForWorker(
BadMessageReceived();
}
} else {
- frontend_proxy_.OnCacheSelected(host_id, appcache::AppCacheInfo());
+ frontend_proxy_.OnCacheSelected(host_id, AppCacheInfo());
}
}
@@ -125,7 +125,7 @@ void AppCacheDispatcherHost::OnSelectCacheForSharedWorker(
if (!backend_impl_.SelectCacheForSharedWorker(host_id, appcache_id))
BadMessageReceived();
} else {
- frontend_proxy_.OnCacheSelected(host_id, appcache::AppCacheInfo());
+ frontend_proxy_.OnCacheSelected(host_id, AppCacheInfo());
}
}
@@ -141,7 +141,7 @@ void AppCacheDispatcherHost::OnMarkAsForeignEntry(
}
void AppCacheDispatcherHost::OnGetResourceList(
- int host_id, std::vector<appcache::AppCacheResourceInfo>* params) {
+ int host_id, std::vector<AppCacheResourceInfo>* params) {
if (appcache_service_.get())
backend_impl_.GetResourceList(host_id, params);
}
@@ -162,7 +162,7 @@ void AppCacheDispatcherHost::OnGetStatus(int host_id, IPC::Message* reply_msg) {
return;
}
- GetStatusCallback(appcache::APPCACHE_STATUS_UNCACHED, reply_msg);
+ GetStatusCallback(APPCACHE_STATUS_UNCACHED, reply_msg);
}
void AppCacheDispatcherHost::OnStartUpdate(int host_id,
@@ -205,7 +205,7 @@ void AppCacheDispatcherHost::OnSwapCache(int host_id, IPC::Message* reply_msg) {
}
void AppCacheDispatcherHost::GetStatusCallback(
- appcache::AppCacheStatus status, void* param) {
+ AppCacheStatus status, void* param) {
IPC::Message* reply_msg = reinterpret_cast<IPC::Message*>(param);
DCHECK_EQ(pending_reply_msg_.get(), reply_msg);
AppCacheHostMsg_GetStatus::WriteReplyParams(reply_msg, status);
diff --git a/content/browser/appcache/appcache_dispatcher_host.h b/content/browser/appcache/appcache_dispatcher_host.h
index 8bad796..3e32898 100644
--- a/content/browser/appcache/appcache_dispatcher_host.h
+++ b/content/browser/appcache/appcache_dispatcher_host.h
@@ -10,9 +10,9 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/process/process.h"
+#include "content/browser/appcache/appcache_backend_impl.h"
#include "content/browser/appcache/appcache_frontend_proxy.h"
#include "content/public/browser/browser_message_filter.h"
-#include "webkit/browser/appcache/appcache_backend_impl.h"
namespace content {
class ChromeAppCacheService;
@@ -54,19 +54,19 @@ class AppCacheDispatcherHost : public BrowserMessageFilter {
void OnSwapCache(int host_id, IPC::Message* reply_msg);
void OnGetResourceList(
int host_id,
- std::vector<appcache::AppCacheResourceInfo>* resource_infos);
- void GetStatusCallback(appcache::AppCacheStatus status, void* param);
+ std::vector<AppCacheResourceInfo>* resource_infos);
+ void GetStatusCallback(AppCacheStatus status, void* param);
void StartUpdateCallback(bool result, void* param);
void SwapCacheCallback(bool result, void* param);
scoped_refptr<ChromeAppCacheService> appcache_service_;
AppCacheFrontendProxy frontend_proxy_;
- appcache::AppCacheBackendImpl backend_impl_;
+ AppCacheBackendImpl backend_impl_;
- appcache::GetStatusCallback get_status_callback_;
- appcache::StartUpdateCallback start_update_callback_;
- appcache::SwapCacheCallback swap_cache_callback_;
+ content::GetStatusCallback get_status_callback_;
+ content::StartUpdateCallback start_update_callback_;
+ content::SwapCacheCallback swap_cache_callback_;
scoped_ptr<IPC::Message> pending_reply_msg_;
// The corresponding ChildProcessHost object's id().
diff --git a/content/browser/appcache/appcache_entry.h b/content/browser/appcache/appcache_entry.h
new file mode 100644
index 0000000..5c4fffd
--- /dev/null
+++ b/content/browser/appcache/appcache_entry.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2009 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.
+
+#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_ENTRY_H_
+#define CONTENT_BROWSER_APPCACHE_APPCACHE_ENTRY_H_
+
+#include "content/common/appcache_interfaces.h"
+
+namespace content {
+
+// A cached entry is identified by a URL and is classified into one
+// (or more) categories. URL is not stored here as this class is stored
+// with the URL as a map key or the user of this class already knows the URL.
+class AppCacheEntry {
+ public:
+
+ // An entry can be of more than one type so use a bitmask.
+ // Note: These bit values are stored on disk.
+ enum Type {
+ MASTER = 1 << 0,
+ MANIFEST = 1 << 1,
+ EXPLICIT = 1 << 2,
+ FOREIGN = 1 << 3,
+ FALLBACK = 1 << 4,
+ INTERCEPT = 1 << 5,
+ EXECUTABLE = 1 << 6,
+ };
+
+ AppCacheEntry()
+ : types_(0), response_id_(kAppCacheNoResponseId), response_size_(0) {}
+
+ explicit AppCacheEntry(int type)
+ : types_(type), response_id_(kAppCacheNoResponseId), response_size_(0) {}
+
+ AppCacheEntry(int type, int64 response_id)
+ : types_(type), response_id_(response_id), response_size_(0) {}
+
+ AppCacheEntry(int type, int64 response_id, int64 response_size)
+ : types_(type), response_id_(response_id), response_size_(response_size) {}
+
+ int types() const { return types_; }
+ void add_types(int added_types) { types_ |= added_types; }
+ bool IsMaster() const { return (types_ & MASTER) != 0; }
+ bool IsManifest() const { return (types_ & MANIFEST) != 0; }
+ bool IsExplicit() const { return (types_ & EXPLICIT) != 0; }
+ bool IsForeign() const { return (types_ & FOREIGN) != 0; }
+ bool IsFallback() const { return (types_ & FALLBACK) != 0; }
+ bool IsIntercept() const { return (types_ & INTERCEPT) != 0; }
+ bool IsExecutable() const { return (types_ & EXECUTABLE) != 0; }
+
+ int64 response_id() const { return response_id_; }
+ void set_response_id(int64 id) { response_id_ = id; }
+ bool has_response_id() const { return response_id_ != kAppCacheNoResponseId; }
+
+ int64 response_size() const { return response_size_; }
+ void set_response_size(int64 size) { response_size_ = size; }
+
+ private:
+ int types_;
+ int64 response_id_;
+ int64 response_size_;
+};
+
+} // namespace content
+
+#endif // WEBKIT_APPCACHE_APPCACHE_RESOURCE_H_
diff --git a/content/browser/appcache/appcache_executable_handler.h b/content/browser/appcache/appcache_executable_handler.h
new file mode 100644
index 0000000..445a6cd
--- /dev/null
+++ b/content/browser/appcache/appcache_executable_handler.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2013 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.
+
+#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_EXECUTABLE_HANDLER_H_
+#define CONTENT_BROWSER_APPCACHE_APPCACHE_EXECUTABLE_HANDLER_H_
+
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/common/content_export.h"
+#include "url/gurl.h"
+
+namespace net {
+class IOBuffer;
+class URLRequest;
+}
+
+namespace content {
+
+// An interface that must be provided by the embedder to support this feature.
+class CONTENT_EXPORT AppCacheExecutableHandler {
+ public:
+ // A handler can respond in one of 4 ways, if each of the GURL fields
+ // in 'Response' are empty and use_network is false, an error response is
+ // synthesized.
+ struct Response {
+ GURL cached_resource_url;
+ GURL redirect_url;
+ bool use_network;
+ // TODO: blob + headers would be a good one to provide as well, as it would
+ // make templating possible.
+ };
+ typedef base::Callback<void(const Response&)> ResponseCallback;
+
+ // Deletion of the handler cancels all pending callbacks.
+ virtual ~AppCacheExecutableHandler() {}
+
+ virtual void HandleRequest(net::URLRequest* req,
+ ResponseCallback callback) = 0;
+};
+
+// A factory to produce instances.
+class CONTENT_EXPORT AppCacheExecutableHandlerFactory {
+ public:
+ virtual scoped_ptr<AppCacheExecutableHandler> CreateHandler(
+ const GURL& handler_url, net::IOBuffer* handler_source) = 0;
+
+ protected:
+ virtual ~AppCacheExecutableHandlerFactory() {}
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_APPCACHE_APPCACHE_EXECUTABLE_HANDLER_H_
diff --git a/content/browser/appcache/appcache_frontend_proxy.cc b/content/browser/appcache/appcache_frontend_proxy.cc
index ea693e9d..4242304 100644
--- a/content/browser/appcache/appcache_frontend_proxy.cc
+++ b/content/browser/appcache/appcache_frontend_proxy.cc
@@ -13,18 +13,18 @@ AppCacheFrontendProxy::AppCacheFrontendProxy(IPC::Sender* sender)
}
void AppCacheFrontendProxy::OnCacheSelected(
- int host_id, const appcache::AppCacheInfo& info) {
+ int host_id, const AppCacheInfo& info) {
sender_->Send(new AppCacheMsg_CacheSelected(host_id, info));
}
void AppCacheFrontendProxy::OnStatusChanged(const std::vector<int>& host_ids,
- appcache::AppCacheStatus status) {
+ AppCacheStatus status) {
sender_->Send(new AppCacheMsg_StatusChanged(host_ids, status));
}
void AppCacheFrontendProxy::OnEventRaised(const std::vector<int>& host_ids,
- appcache::AppCacheEventID event_id) {
- DCHECK_NE(appcache::APPCACHE_PROGRESS_EVENT,
+ AppCacheEventID event_id) {
+ DCHECK_NE(APPCACHE_PROGRESS_EVENT,
event_id); // See OnProgressEventRaised.
sender_->Send(new AppCacheMsg_EventRaised(host_ids, event_id));
}
@@ -38,12 +38,12 @@ void AppCacheFrontendProxy::OnProgressEventRaised(
void AppCacheFrontendProxy::OnErrorEventRaised(
const std::vector<int>& host_ids,
- const appcache::AppCacheErrorDetails& details) {
+ const AppCacheErrorDetails& details) {
sender_->Send(new AppCacheMsg_ErrorEventRaised(host_ids, details));
}
void AppCacheFrontendProxy::OnLogMessage(int host_id,
- appcache::AppCacheLogLevel log_level,
+ AppCacheLogLevel log_level,
const std::string& message) {
sender_->Send(new AppCacheMsg_LogMessage(host_id, log_level, message));
}
diff --git a/content/browser/appcache/appcache_frontend_proxy.h b/content/browser/appcache/appcache_frontend_proxy.h
index 904c8ae..fdd05cd 100644
--- a/content/browser/appcache/appcache_frontend_proxy.h
+++ b/content/browser/appcache/appcache_frontend_proxy.h
@@ -8,30 +8,30 @@
#include <string>
#include <vector>
+#include "content/common/appcache_interfaces.h"
#include "ipc/ipc_sender.h"
-#include "webkit/common/appcache/appcache_interfaces.h"
namespace content {
// Sends appcache related messages to a child process.
-class AppCacheFrontendProxy : public appcache::AppCacheFrontend {
+class AppCacheFrontendProxy : public AppCacheFrontend {
public:
explicit AppCacheFrontendProxy(IPC::Sender* sender);
// AppCacheFrontend methods
virtual void OnCacheSelected(int host_id,
- const appcache::AppCacheInfo& info) OVERRIDE;
+ const AppCacheInfo& info) OVERRIDE;
virtual void OnStatusChanged(const std::vector<int>& host_ids,
- appcache::AppCacheStatus status) OVERRIDE;
+ AppCacheStatus status) OVERRIDE;
virtual void OnEventRaised(const std::vector<int>& host_ids,
- appcache::AppCacheEventID event_id) OVERRIDE;
+ AppCacheEventID event_id) OVERRIDE;
virtual void OnProgressEventRaised(const std::vector<int>& host_ids,
const GURL& url,
int num_total, int num_complete) OVERRIDE;
virtual void OnErrorEventRaised(const std::vector<int>& host_ids,
- const appcache::AppCacheErrorDetails& details)
+ const AppCacheErrorDetails& details)
OVERRIDE;
- virtual void OnLogMessage(int host_id, appcache::AppCacheLogLevel log_level,
+ virtual void OnLogMessage(int host_id, AppCacheLogLevel log_level,
const std::string& message) OVERRIDE;
virtual void OnContentBlocked(int host_id,
const GURL& manifest_url) OVERRIDE;
diff --git a/content/browser/appcache/appcache_group.cc b/content/browser/appcache/appcache_group.cc
new file mode 100644
index 0000000..60a1024
--- /dev/null
+++ b/content/browser/appcache/appcache_group.cc
@@ -0,0 +1,264 @@
+// Copyright (c) 2012 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 "content/browser/appcache/appcache_group.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "content/browser/appcache/appcache.h"
+#include "content/browser/appcache/appcache_host.h"
+#include "content/browser/appcache/appcache_service_impl.h"
+#include "content/browser/appcache/appcache_storage.h"
+#include "content/browser/appcache/appcache_update_job.h"
+
+namespace content {
+
+class AppCacheGroup;
+
+// Use this helper class because we cannot make AppCacheGroup a derived class
+// of AppCacheHost::Observer as it would create a circular dependency between
+// AppCacheHost and AppCacheGroup.
+class AppCacheGroup::HostObserver : public AppCacheHost::Observer {
+ public:
+ explicit HostObserver(AppCacheGroup* group) : group_(group) {}
+
+ // Methods for AppCacheHost::Observer.
+ virtual void OnCacheSelectionComplete(AppCacheHost* host) OVERRIDE {} // N/A
+ virtual void OnDestructionImminent(AppCacheHost* host) OVERRIDE {
+ group_->HostDestructionImminent(host);
+ }
+ private:
+ AppCacheGroup* group_;
+};
+
+AppCacheGroup::AppCacheGroup(AppCacheStorage* storage,
+ const GURL& manifest_url,
+ int64 group_id)
+ : group_id_(group_id),
+ manifest_url_(manifest_url),
+ update_status_(IDLE),
+ is_obsolete_(false),
+ is_being_deleted_(false),
+ newest_complete_cache_(NULL),
+ update_job_(NULL),
+ storage_(storage),
+ is_in_dtor_(false) {
+ storage_->working_set()->AddGroup(this);
+ host_observer_.reset(new HostObserver(this));
+}
+
+AppCacheGroup::~AppCacheGroup() {
+ DCHECK(old_caches_.empty());
+ DCHECK(!newest_complete_cache_);
+ DCHECK(restart_update_task_.IsCancelled());
+ DCHECK(queued_updates_.empty());
+
+ is_in_dtor_ = true;
+
+ if (update_job_)
+ delete update_job_;
+ DCHECK_EQ(IDLE, update_status_);
+
+ storage_->working_set()->RemoveGroup(this);
+ storage_->DeleteResponses(manifest_url_, newly_deletable_response_ids_);
+}
+
+void AppCacheGroup::AddUpdateObserver(UpdateObserver* observer) {
+ // If observer being added is a host that has been queued for later update,
+ // add observer to a different observer list.
+ AppCacheHost* host = static_cast<AppCacheHost*>(observer);
+ if (queued_updates_.find(host) != queued_updates_.end())
+ queued_observers_.AddObserver(observer);
+ else
+ observers_.AddObserver(observer);
+}
+
+void AppCacheGroup::RemoveUpdateObserver(UpdateObserver* observer) {
+ observers_.RemoveObserver(observer);
+ queued_observers_.RemoveObserver(observer);
+}
+
+void AppCacheGroup::AddCache(AppCache* complete_cache) {
+ DCHECK(complete_cache->is_complete());
+ complete_cache->set_owning_group(this);
+
+ if (!newest_complete_cache_) {
+ newest_complete_cache_ = complete_cache;
+ return;
+ }
+
+ if (complete_cache->IsNewerThan(newest_complete_cache_)) {
+ old_caches_.push_back(newest_complete_cache_);
+ newest_complete_cache_ = complete_cache;
+
+ // Update hosts of older caches to add a reference to the newest cache.
+ for (Caches::iterator it = old_caches_.begin();
+ it != old_caches_.end(); ++it) {
+ AppCache::AppCacheHosts& hosts = (*it)->associated_hosts();
+ for (AppCache::AppCacheHosts::iterator host_it = hosts.begin();
+ host_it != hosts.end(); ++host_it) {
+ (*host_it)->SetSwappableCache(this);
+ }
+ }
+ } else {
+ old_caches_.push_back(complete_cache);
+ }
+}
+
+void AppCacheGroup::RemoveCache(AppCache* cache) {
+ DCHECK(cache->associated_hosts().empty());
+ if (cache == newest_complete_cache_) {
+ AppCache* tmp_cache = newest_complete_cache_;
+ newest_complete_cache_ = NULL;
+ tmp_cache->set_owning_group(NULL); // may cause this group to be deleted
+ } else {
+ scoped_refptr<AppCacheGroup> protect(this);
+
+ Caches::iterator it =
+ std::find(old_caches_.begin(), old_caches_.end(), cache);
+ if (it != old_caches_.end()) {
+ AppCache* tmp_cache = *it;
+ old_caches_.erase(it);
+ tmp_cache->set_owning_group(NULL); // may cause group to be released
+ }
+
+ if (!is_obsolete() && old_caches_.empty() &&
+ !newly_deletable_response_ids_.empty()) {
+ storage_->DeleteResponses(manifest_url_, newly_deletable_response_ids_);
+ newly_deletable_response_ids_.clear();
+ }
+ }
+}
+
+void AppCacheGroup::AddNewlyDeletableResponseIds(
+ std::vector<int64>* response_ids) {
+ if (is_being_deleted() || (!is_obsolete() && old_caches_.empty())) {
+ storage_->DeleteResponses(manifest_url_, *response_ids);
+ response_ids->clear();
+ return;
+ }
+
+ if (newly_deletable_response_ids_.empty()) {
+ newly_deletable_response_ids_.swap(*response_ids);
+ return;
+ }
+ newly_deletable_response_ids_.insert(
+ newly_deletable_response_ids_.end(),
+ response_ids->begin(), response_ids->end());
+ response_ids->clear();
+}
+
+void AppCacheGroup::StartUpdateWithNewMasterEntry(
+ AppCacheHost* host, const GURL& new_master_resource) {
+ DCHECK(!is_obsolete() && !is_being_deleted());
+ if (is_in_dtor_)
+ return;
+
+ if (!update_job_)
+ update_job_ = new AppCacheUpdateJob(storage_->service(), this);
+
+ update_job_->StartUpdate(host, new_master_resource);
+
+ // Run queued update immediately as an update has been started manually.
+ if (!restart_update_task_.IsCancelled()) {
+ restart_update_task_.Cancel();
+ RunQueuedUpdates();
+ }
+}
+
+void AppCacheGroup::CancelUpdate() {
+ if (update_job_) {
+ delete update_job_;
+ DCHECK(!update_job_);
+ DCHECK_EQ(IDLE, update_status_);
+ }
+}
+
+void AppCacheGroup::QueueUpdate(AppCacheHost* host,
+ const GURL& new_master_resource) {
+ DCHECK(update_job_ && host && !new_master_resource.is_empty());
+ queued_updates_.insert(QueuedUpdates::value_type(host, new_master_resource));
+
+ // Need to know when host is destroyed.
+ host->AddObserver(host_observer_.get());
+
+ // If host is already observing for updates, move host to queued observers
+ // list so that host is not notified when the current update completes.
+ if (FindObserver(host, observers_)) {
+ observers_.RemoveObserver(host);
+ queued_observers_.AddObserver(host);
+ }
+}
+
+void AppCacheGroup::RunQueuedUpdates() {
+ if (!restart_update_task_.IsCancelled())
+ restart_update_task_.Cancel();
+
+ if (queued_updates_.empty())
+ return;
+
+ QueuedUpdates updates_to_run;
+ queued_updates_.swap(updates_to_run);
+ DCHECK(queued_updates_.empty());
+
+ for (QueuedUpdates::iterator it = updates_to_run.begin();
+ it != updates_to_run.end(); ++it) {
+ AppCacheHost* host = it->first;
+ host->RemoveObserver(host_observer_.get());
+ if (FindObserver(host, queued_observers_)) {
+ queued_observers_.RemoveObserver(host);
+ observers_.AddObserver(host);
+ }
+
+ if (!is_obsolete() && !is_being_deleted())
+ StartUpdateWithNewMasterEntry(host, it->second);
+ }
+}
+
+bool AppCacheGroup::FindObserver(UpdateObserver* find_me,
+ const ObserverList<UpdateObserver>& observer_list) {
+ return observer_list.HasObserver(find_me);
+}
+
+void AppCacheGroup::ScheduleUpdateRestart(int delay_ms) {
+ DCHECK(restart_update_task_.IsCancelled());
+ restart_update_task_.Reset(
+ base::Bind(&AppCacheGroup::RunQueuedUpdates, this));
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ restart_update_task_.callback(),
+ base::TimeDelta::FromMilliseconds(delay_ms));
+}
+
+void AppCacheGroup::HostDestructionImminent(AppCacheHost* host) {
+ queued_updates_.erase(host);
+ if (queued_updates_.empty() && !restart_update_task_.IsCancelled())
+ restart_update_task_.Cancel();
+}
+
+void AppCacheGroup::SetUpdateAppCacheStatus(UpdateAppCacheStatus status) {
+ if (status == update_status_)
+ return;
+
+ update_status_ = status;
+
+ if (status != IDLE) {
+ DCHECK(update_job_);
+ } else {
+ update_job_ = NULL;
+
+ // Observers may release us in these callbacks, so we protect against
+ // deletion by adding an extra ref in this scope (but only if we're not
+ // in our destructor).
+ scoped_refptr<AppCacheGroup> protect(is_in_dtor_ ? NULL : this);
+ FOR_EACH_OBSERVER(UpdateObserver, observers_, OnUpdateComplete(this));
+ if (!queued_updates_.empty())
+ ScheduleUpdateRestart(kUpdateRestartDelayMs);
+ }
+}
+
+} // namespace content
diff --git a/content/browser/appcache/appcache_group.h b/content/browser/appcache/appcache_group.h
new file mode 100644
index 0000000..c79318f
--- /dev/null
+++ b/content/browser/appcache/appcache_group.h
@@ -0,0 +1,177 @@
+// Copyright (c) 2011 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.
+
+#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_GROUP_H_
+#define CONTENT_BROWSER_APPCACHE_APPCACHE_GROUP_H_
+
+#include <map>
+#include <vector>
+
+#include "base/cancelable_callback.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "base/time/time.h"
+#include "content/common/content_export.h"
+#include "url/gurl.h"
+
+namespace content {
+FORWARD_DECLARE_TEST(AppCacheGroupTest, StartUpdate);
+FORWARD_DECLARE_TEST(AppCacheGroupTest, CancelUpdate);
+FORWARD_DECLARE_TEST(AppCacheGroupTest, QueueUpdate);
+FORWARD_DECLARE_TEST(AppCacheUpdateJobTest, AlreadyChecking);
+FORWARD_DECLARE_TEST(AppCacheUpdateJobTest, AlreadyDownloading);
+class AppCacheUpdateJobTest;
+class MockAppCacheStorage;
+}
+
+namespace content {
+
+class AppCache;
+class AppCacheHost;
+class AppCacheStorage;
+class AppCacheUpdateJob;
+class HostObserver;
+
+// Collection of application caches identified by the same manifest URL.
+// A group exists as long as it is in use by a host or is being updated.
+class CONTENT_EXPORT AppCacheGroup
+ : public base::RefCounted<AppCacheGroup> {
+ public:
+
+ class CONTENT_EXPORT UpdateObserver {
+ public:
+ // Called just after an appcache update has completed.
+ virtual void OnUpdateComplete(AppCacheGroup* group) = 0;
+ virtual ~UpdateObserver() {}
+ };
+
+ enum UpdateAppCacheStatus {
+ IDLE,
+ CHECKING,
+ DOWNLOADING,
+ };
+
+ AppCacheGroup(AppCacheStorage* storage, const GURL& manifest_url,
+ int64 group_id);
+
+ // Adds/removes an update observer, the AppCacheGroup does not take
+ // ownership of the observer.
+ void AddUpdateObserver(UpdateObserver* observer);
+ void RemoveUpdateObserver(UpdateObserver* observer);
+
+ int64 group_id() const { return group_id_; }
+ const GURL& manifest_url() const { return manifest_url_; }
+ const base::Time& creation_time() const { return creation_time_; }
+ void set_creation_time(const base::Time& time) { creation_time_ = time; }
+ bool is_obsolete() const { return is_obsolete_; }
+ void set_obsolete(bool value) { is_obsolete_ = value; }
+
+ bool is_being_deleted() const { return is_being_deleted_; }
+ void set_being_deleted(bool value) { is_being_deleted_ = value; }
+
+ AppCache* newest_complete_cache() const { return newest_complete_cache_; }
+
+ void AddCache(AppCache* complete_cache);
+ void RemoveCache(AppCache* cache);
+ bool HasCache() const { return newest_complete_cache_ != NULL; }
+
+ void AddNewlyDeletableResponseIds(std::vector<int64>* response_ids);
+
+ UpdateAppCacheStatus update_status() const { return update_status_; }
+
+ // Starts an update via update() javascript API.
+ void StartUpdate() {
+ StartUpdateWithHost(NULL);
+ }
+
+ // Starts an update for a doc loaded from an application cache.
+ void StartUpdateWithHost(AppCacheHost* host) {
+ StartUpdateWithNewMasterEntry(host, GURL());
+ }
+
+ // Starts an update for a doc loaded using HTTP GET or equivalent with
+ // an <html> tag manifest attribute value that matches this group's
+ // manifest url.
+ void StartUpdateWithNewMasterEntry(AppCacheHost* host,
+ const GURL& new_master_resource);
+
+ // Cancels an update if one is running.
+ void CancelUpdate();
+
+ private:
+ class HostObserver;
+
+ friend class base::RefCounted<AppCacheGroup>;
+ friend class content::AppCacheUpdateJobTest;
+ friend class content::MockAppCacheStorage; // for old_caches()
+ friend class AppCacheUpdateJob;
+
+ ~AppCacheGroup();
+
+ typedef std::vector<AppCache*> Caches;
+ typedef std::map<AppCacheHost*, GURL> QueuedUpdates;
+
+ static const int kUpdateRestartDelayMs = 1000;
+
+ AppCacheUpdateJob* update_job() { return update_job_; }
+ void SetUpdateAppCacheStatus(UpdateAppCacheStatus status);
+
+ void NotifyContentBlocked();
+
+ const Caches& old_caches() const { return old_caches_; }
+
+ // Update cannot be processed at this time. Queue it for a later run.
+ void QueueUpdate(AppCacheHost* host, const GURL& new_master_resource);
+ void RunQueuedUpdates();
+ bool FindObserver(UpdateObserver* find_me,
+ const ObserverList<UpdateObserver>& observer_list);
+ void ScheduleUpdateRestart(int delay_ms);
+ void HostDestructionImminent(AppCacheHost* host);
+
+ const int64 group_id_;
+ const GURL manifest_url_;
+ base::Time creation_time_;
+ UpdateAppCacheStatus update_status_;
+ bool is_obsolete_;
+ bool is_being_deleted_;
+ std::vector<int64> newly_deletable_response_ids_;
+
+ // Old complete app caches.
+ Caches old_caches_;
+
+ // Newest cache in this group to be complete, aka relevant cache.
+ AppCache* newest_complete_cache_;
+
+ // Current update job for this group, if any.
+ AppCacheUpdateJob* update_job_;
+
+ // Central storage object.
+ AppCacheStorage* storage_;
+
+ // List of objects observing this group.
+ ObserverList<UpdateObserver> observers_;
+
+ // Updates that have been queued for the next run.
+ QueuedUpdates queued_updates_;
+ ObserverList<UpdateObserver> queued_observers_;
+ base::CancelableClosure restart_update_task_;
+ scoped_ptr<HostObserver> host_observer_;
+
+ // True if we're in our destructor.
+ bool is_in_dtor_;
+
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheGroupTest, StartUpdate);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheGroupTest, CancelUpdate);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheGroupTest, QueueUpdate);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheUpdateJobTest, AlreadyChecking);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheUpdateJobTest, AlreadyDownloading);
+
+ DISALLOW_COPY_AND_ASSIGN(AppCacheGroup);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_APPCACHE_APPCACHE_GROUP_H_
diff --git a/content/browser/appcache/appcache_group_unittest.cc b/content/browser/appcache/appcache_group_unittest.cc
index cec20fa..ad45b3b 100644
--- a/content/browser/appcache/appcache_group_unittest.cc
+++ b/content/browser/appcache/appcache_group_unittest.cc
@@ -5,47 +5,40 @@
#include <string>
#include "base/message_loop/message_loop.h"
+#include "content/browser/appcache/appcache.h"
+#include "content/browser/appcache/appcache_group.h"
+#include "content/browser/appcache/appcache_host.h"
+#include "content/browser/appcache/appcache_update_job.h"
#include "content/browser/appcache/mock_appcache_service.h"
+#include "content/common/appcache_interfaces.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/appcache/appcache.h"
-#include "webkit/browser/appcache/appcache_group.h"
-#include "webkit/browser/appcache/appcache_host.h"
-#include "webkit/browser/appcache/appcache_update_job.h"
-#include "webkit/common/appcache/appcache_interfaces.h"
-
-using appcache::AppCache;
-using appcache::AppCacheFrontend;
-using appcache::AppCacheGroup;
-using appcache::AppCacheHost;
-using appcache::AppCacheServiceImpl;
-using appcache::AppCacheUpdateJob;
namespace {
-class TestAppCacheFrontend : public appcache::AppCacheFrontend {
+class TestAppCacheFrontend : public content::AppCacheFrontend {
public:
TestAppCacheFrontend()
: last_host_id_(-1), last_cache_id_(-1),
- last_status_(appcache::APPCACHE_STATUS_OBSOLETE) {
+ last_status_(content::APPCACHE_STATUS_OBSOLETE) {
}
virtual void OnCacheSelected(
- int host_id, const appcache::AppCacheInfo& info) OVERRIDE {
+ int host_id, const content::AppCacheInfo& info) OVERRIDE {
last_host_id_ = host_id;
last_cache_id_ = info.cache_id;
last_status_ = info.status;
}
virtual void OnStatusChanged(const std::vector<int>& host_ids,
- appcache::AppCacheStatus status) OVERRIDE {
+ content::AppCacheStatus status) OVERRIDE {
}
virtual void OnEventRaised(const std::vector<int>& host_ids,
- appcache::AppCacheEventID event_id) OVERRIDE {
+ content::AppCacheEventID event_id) OVERRIDE {
}
virtual void OnErrorEventRaised(const std::vector<int>& host_ids,
- const appcache::AppCacheErrorDetails& details)
+ const content::AppCacheErrorDetails& details)
OVERRIDE {}
virtual void OnProgressEventRaised(const std::vector<int>& host_ids,
@@ -53,7 +46,7 @@ class TestAppCacheFrontend : public appcache::AppCacheFrontend {
int num_total, int num_complete) OVERRIDE {
}
- virtual void OnLogMessage(int host_id, appcache::AppCacheLogLevel log_level,
+ virtual void OnLogMessage(int host_id, content::AppCacheLogLevel log_level,
const std::string& message) OVERRIDE {
}
@@ -63,7 +56,7 @@ class TestAppCacheFrontend : public appcache::AppCacheFrontend {
int last_host_id_;
int64 last_cache_id_;
- appcache::AppCacheStatus last_status_;
+ content::AppCacheStatus last_status_;
};
} // namespace anon
@@ -197,12 +190,12 @@ TEST_F(AppCacheGroupTest, CleanupUnusedGroup) {
host1.AssociateCompleteCache(cache1);
EXPECT_EQ(frontend.last_host_id_, host1.host_id());
EXPECT_EQ(frontend.last_cache_id_, cache1->cache_id());
- EXPECT_EQ(frontend.last_status_, appcache::APPCACHE_STATUS_IDLE);
+ EXPECT_EQ(frontend.last_status_, APPCACHE_STATUS_IDLE);
host2.AssociateCompleteCache(cache1);
EXPECT_EQ(frontend.last_host_id_, host2.host_id());
EXPECT_EQ(frontend.last_cache_id_, cache1->cache_id());
- EXPECT_EQ(frontend.last_status_, appcache::APPCACHE_STATUS_IDLE);
+ EXPECT_EQ(frontend.last_status_, APPCACHE_STATUS_IDLE);
AppCache* cache2 = new AppCache(service.storage(), 222);
cache2->set_complete(true);
@@ -214,8 +207,8 @@ TEST_F(AppCacheGroupTest, CleanupUnusedGroup) {
host1.AssociateNoCache(GURL());
host2.AssociateNoCache(GURL());
EXPECT_EQ(frontend.last_host_id_, host2.host_id());
- EXPECT_EQ(frontend.last_cache_id_, appcache::kAppCacheNoCacheId);
- EXPECT_EQ(frontend.last_status_, appcache::APPCACHE_STATUS_UNCACHED);
+ EXPECT_EQ(frontend.last_cache_id_, kAppCacheNoCacheId);
+ EXPECT_EQ(frontend.last_status_, APPCACHE_STATUS_UNCACHED);
}
TEST_F(AppCacheGroupTest, StartUpdate) {
diff --git a/content/browser/appcache/appcache_histograms.cc b/content/browser/appcache/appcache_histograms.cc
new file mode 100644
index 0000000..ff221b0
--- /dev/null
+++ b/content/browser/appcache/appcache_histograms.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2012 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 "content/browser/appcache/appcache_histograms.h"
+
+#include "base/metrics/histogram.h"
+
+namespace content {
+
+static std::string OriginToCustomHistogramSuffix(const GURL& origin_url) {
+ if (origin_url.host() == "docs.google.com")
+ return ".Docs";
+ return std::string();
+}
+
+void AppCacheHistograms::CountInitResult(InitResultType init_result) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "appcache.InitResult",
+ init_result, NUM_INIT_RESULT_TYPES);
+}
+
+void AppCacheHistograms::CountReinitAttempt(bool repeated_attempt) {
+ UMA_HISTOGRAM_BOOLEAN("appcache.ReinitAttempt", repeated_attempt);
+}
+
+void AppCacheHistograms::CountCorruptionDetected() {
+ UMA_HISTOGRAM_BOOLEAN("appcache.CorruptionDetected", true);
+}
+
+void AppCacheHistograms::CountUpdateJobResult(
+ AppCacheUpdateJob::ResultType result,
+ const GURL& origin_url) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "appcache.UpdateJobResult",
+ result, AppCacheUpdateJob::NUM_UPDATE_JOB_RESULT_TYPES);
+
+ const std::string suffix = OriginToCustomHistogramSuffix(origin_url);
+ if (!suffix.empty()) {
+ base::LinearHistogram::FactoryGet(
+ "appcache.UpdateJobResult" + suffix,
+ 1,
+ AppCacheUpdateJob::NUM_UPDATE_JOB_RESULT_TYPES,
+ AppCacheUpdateJob::NUM_UPDATE_JOB_RESULT_TYPES + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag)->Add(result);
+ }
+}
+
+void AppCacheHistograms::CountCheckResponseResult(
+ CheckResponseResultType result) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "appcache.CheckResponseResult",
+ result, NUM_CHECK_RESPONSE_RESULT_TYPES);
+}
+
+void AppCacheHistograms::CountResponseRetrieval(
+ bool success, bool is_main_resource, const GURL& origin_url) {
+ std::string label;
+ if (is_main_resource) {
+ label = "appcache.MainResourceResponseRetrieval";
+ UMA_HISTOGRAM_BOOLEAN(label, success);
+ } else {
+ label = "appcache.SubResourceResponseRetrieval";
+ UMA_HISTOGRAM_BOOLEAN(label, success);
+ }
+ const std::string suffix = OriginToCustomHistogramSuffix(origin_url);
+ if (!suffix.empty()) {
+ base::BooleanHistogram::FactoryGet(
+ label + suffix,
+ base::HistogramBase::kUmaTargetedHistogramFlag)->Add(success);
+ }
+}
+
+void AppCacheHistograms::LogUpdateFailureStats(
+ const GURL& origin_url,
+ int percent_complete,
+ bool was_stalled,
+ bool was_off_origin_resource_failure) {
+ const std::string suffix = OriginToCustomHistogramSuffix(origin_url);
+
+ std::string label = "appcache.UpdateProgressAtPointOfFaliure";
+ UMA_HISTOGRAM_PERCENTAGE(label, percent_complete);
+ if (!suffix.empty()) {
+ base::LinearHistogram::FactoryGet(
+ label + suffix,
+ 1, 101, 102,
+ base::HistogramBase::kUmaTargetedHistogramFlag)->Add(percent_complete);
+ }
+
+ label = "appcache.UpdateWasStalledAtPointOfFailure";
+ UMA_HISTOGRAM_BOOLEAN(label, was_stalled);
+ if (!suffix.empty()) {
+ base::BooleanHistogram::FactoryGet(
+ label + suffix,
+ base::HistogramBase::kUmaTargetedHistogramFlag)->Add(was_stalled);
+ }
+
+ label = "appcache.UpdateWasOffOriginAtPointOfFailure";
+ UMA_HISTOGRAM_BOOLEAN(label, was_off_origin_resource_failure);
+ if (!suffix.empty()) {
+ base::BooleanHistogram::FactoryGet(
+ label + suffix,
+ base::HistogramBase::kUmaTargetedHistogramFlag)->Add(
+ was_off_origin_resource_failure);
+ }
+}
+
+void AppCacheHistograms::AddTaskQueueTimeSample(
+ const base::TimeDelta& duration) {
+ UMA_HISTOGRAM_TIMES("appcache.TaskQueueTime", duration);
+}
+
+void AppCacheHistograms::AddTaskRunTimeSample(
+ const base::TimeDelta& duration) {
+ UMA_HISTOGRAM_TIMES("appcache.TaskRunTime", duration);
+}
+
+void AppCacheHistograms::AddCompletionQueueTimeSample(
+ const base::TimeDelta& duration) {
+ UMA_HISTOGRAM_TIMES("appcache.CompletionQueueTime", duration);
+}
+
+void AppCacheHistograms::AddCompletionRunTimeSample(
+ const base::TimeDelta& duration) {
+ UMA_HISTOGRAM_TIMES("appcache.CompletionRunTime", duration);
+}
+
+void AppCacheHistograms::AddNetworkJobStartDelaySample(
+ const base::TimeDelta& duration) {
+ UMA_HISTOGRAM_TIMES("appcache.JobStartDelay.Network", duration);
+}
+
+void AppCacheHistograms::AddErrorJobStartDelaySample(
+ const base::TimeDelta& duration) {
+ UMA_HISTOGRAM_TIMES("appcache.JobStartDelay.Error", duration);
+}
+
+void AppCacheHistograms::AddAppCacheJobStartDelaySample(
+ const base::TimeDelta& duration) {
+ UMA_HISTOGRAM_TIMES("appcache.JobStartDelay.AppCache", duration);
+}
+
+void AppCacheHistograms::AddMissingManifestEntrySample() {
+ UMA_HISTOGRAM_BOOLEAN("appcache.MissingManifestEntry", true);
+}
+
+void AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
+ MissingManifestCallsiteType callsite) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "appcache.MissingManifestDetectedAtCallsite",
+ callsite, NUM_MISSING_MANIFEST_CALLSITE_TYPES);
+}
+
+} // namespace content
diff --git a/content/browser/appcache/appcache_histograms.h b/content/browser/appcache/appcache_histograms.h
new file mode 100644
index 0000000..0fcc54c
--- /dev/null
+++ b/content/browser/appcache/appcache_histograms.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2012 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.
+
+#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_HISTOGRAMS_H_
+#define CONTENT_BROWSER_APPCACHE_APPCACHE_HISTOGRAMS_H_
+
+#include "base/basictypes.h"
+#include "content/browser/appcache/appcache_update_job.h"
+
+namespace base {
+class TimeDelta;
+}
+
+namespace content {
+
+class AppCacheHistograms {
+ public:
+ enum InitResultType {
+ INIT_OK, SQL_DATABASE_ERROR, DISK_CACHE_ERROR,
+ NUM_INIT_RESULT_TYPES
+ };
+ static void CountInitResult(InitResultType init_result);
+ static void CountReinitAttempt(bool repeated_attempt);
+ static void CountCorruptionDetected();
+ static void CountUpdateJobResult(AppCacheUpdateJob::ResultType result,
+ const GURL& origin_url);
+ enum CheckResponseResultType {
+ RESPONSE_OK, MANIFEST_OUT_OF_DATE, RESPONSE_OUT_OF_DATE, ENTRY_NOT_FOUND,
+ READ_HEADERS_ERROR, READ_DATA_ERROR, UNEXPECTED_DATA_SIZE, CHECK_CANCELED,
+ NUM_CHECK_RESPONSE_RESULT_TYPES
+ };
+ static void CountCheckResponseResult(CheckResponseResultType result);
+ static void CountResponseRetrieval(
+ bool success, bool is_main_resource, const GURL& origin_url);
+ static void LogUpdateFailureStats(
+ const GURL& origin_url,
+ int percent_complete,
+ bool was_making_progress,
+ bool off_origin_resource_failure);
+ static void AddTaskQueueTimeSample(const base::TimeDelta& duration);
+ static void AddTaskRunTimeSample(const base::TimeDelta& duration);
+ static void AddCompletionQueueTimeSample(const base::TimeDelta& duration);
+ static void AddCompletionRunTimeSample(const base::TimeDelta& duration);
+ static void AddNetworkJobStartDelaySample(const base::TimeDelta& duration);
+ static void AddErrorJobStartDelaySample(const base::TimeDelta& duration);
+ static void AddAppCacheJobStartDelaySample(const base::TimeDelta& duration);
+ static void AddMissingManifestEntrySample();
+
+ enum MissingManifestCallsiteType {
+ CALLSITE_0, CALLSITE_1, CALLSITE_2, CALLSITE_3,
+ NUM_MISSING_MANIFEST_CALLSITE_TYPES
+ };
+ static void AddMissingManifestDetectedAtCallsite(
+ MissingManifestCallsiteType type);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(AppCacheHistograms);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_APPCACHE_APPCACHE_HISTOGRAMS_H_
diff --git a/content/browser/appcache/appcache_host.cc b/content/browser/appcache/appcache_host.cc
new file mode 100644
index 0000000..db04dad
--- /dev/null
+++ b/content/browser/appcache/appcache_host.cc
@@ -0,0 +1,546 @@
+// Copyright (c) 2011 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 "content/browser/appcache/appcache_host.h"
+
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "content/browser/appcache/appcache.h"
+#include "content/browser/appcache/appcache_backend_impl.h"
+#include "content/browser/appcache/appcache_policy.h"
+#include "content/browser/appcache/appcache_request_handler.h"
+#include "net/url_request/url_request.h"
+#include "webkit/browser/quota/quota_manager_proxy.h"
+
+namespace content {
+
+namespace {
+
+void FillCacheInfo(const AppCache* cache,
+ const GURL& manifest_url,
+ AppCacheStatus status, AppCacheInfo* info) {
+ info->manifest_url = manifest_url;
+ info->status = status;
+
+ if (!cache)
+ return;
+
+ info->cache_id = cache->cache_id();
+
+ if (!cache->is_complete())
+ return;
+
+ DCHECK(cache->owning_group());
+ info->is_complete = true;
+ info->group_id = cache->owning_group()->group_id();
+ info->last_update_time = cache->update_time();
+ info->creation_time = cache->owning_group()->creation_time();
+ info->size = cache->cache_size();
+}
+
+} // Anonymous namespace
+
+AppCacheHost::AppCacheHost(int host_id, AppCacheFrontend* frontend,
+ AppCacheServiceImpl* service)
+ : host_id_(host_id),
+ spawning_host_id_(kAppCacheNoHostId), spawning_process_id_(0),
+ parent_host_id_(kAppCacheNoHostId), parent_process_id_(0),
+ pending_main_resource_cache_id_(kAppCacheNoCacheId),
+ pending_selected_cache_id_(kAppCacheNoCacheId),
+ frontend_(frontend), service_(service),
+ storage_(service->storage()),
+ pending_callback_param_(NULL),
+ main_resource_was_namespace_entry_(false),
+ main_resource_blocked_(false),
+ associated_cache_info_pending_(false) {
+ service_->AddObserver(this);
+}
+
+AppCacheHost::~AppCacheHost() {
+ service_->RemoveObserver(this);
+ FOR_EACH_OBSERVER(Observer, observers_, OnDestructionImminent(this));
+ if (associated_cache_.get())
+ associated_cache_->UnassociateHost(this);
+ if (group_being_updated_.get())
+ group_being_updated_->RemoveUpdateObserver(this);
+ storage()->CancelDelegateCallbacks(this);
+ if (service()->quota_manager_proxy() && !origin_in_use_.is_empty())
+ service()->quota_manager_proxy()->NotifyOriginNoLongerInUse(origin_in_use_);
+}
+
+void AppCacheHost::AddObserver(Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void AppCacheHost::RemoveObserver(Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void AppCacheHost::SelectCache(const GURL& document_url,
+ const int64 cache_document_was_loaded_from,
+ const GURL& manifest_url) {
+ DCHECK(pending_start_update_callback_.is_null() &&
+ pending_swap_cache_callback_.is_null() &&
+ pending_get_status_callback_.is_null() &&
+ !is_selection_pending());
+
+ origin_in_use_ = document_url.GetOrigin();
+ if (service()->quota_manager_proxy() && !origin_in_use_.is_empty())
+ service()->quota_manager_proxy()->NotifyOriginInUse(origin_in_use_);
+
+ if (main_resource_blocked_)
+ frontend_->OnContentBlocked(host_id_,
+ blocked_manifest_url_);
+
+ // 6.9.6 The application cache selection algorithm.
+ // The algorithm is started here and continues in FinishCacheSelection,
+ // after cache or group loading is complete.
+ // Note: Foreign entries are detected on the client side and
+ // MarkAsForeignEntry is called in that case, so that detection
+ // step is skipped here. See WebApplicationCacheHostImpl.cc
+
+ if (cache_document_was_loaded_from != kAppCacheNoCacheId) {
+ LoadSelectedCache(cache_document_was_loaded_from);
+ return;
+ }
+
+ if (!manifest_url.is_empty() &&
+ (manifest_url.GetOrigin() == document_url.GetOrigin())) {
+ DCHECK(!first_party_url_.is_empty());
+ AppCachePolicy* policy = service()->appcache_policy();
+ if (policy &&
+ !policy->CanCreateAppCache(manifest_url, first_party_url_)) {
+ FinishCacheSelection(NULL, NULL);
+ std::vector<int> host_ids(1, host_id_);
+ frontend_->OnEventRaised(host_ids, APPCACHE_CHECKING_EVENT);
+ frontend_->OnErrorEventRaised(
+ host_ids,
+ AppCacheErrorDetails(
+ "Cache creation was blocked by the content policy",
+ APPCACHE_POLICY_ERROR,
+ GURL(),
+ 0,
+ false /*is_cross_origin*/));
+ frontend_->OnContentBlocked(host_id_, manifest_url);
+ return;
+ }
+
+ // Note: The client detects if the document was not loaded using HTTP GET
+ // and invokes SelectCache without a manifest url, so that detection step
+ // is also skipped here. See WebApplicationCacheHostImpl.cc
+ set_preferred_manifest_url(manifest_url);
+ new_master_entry_url_ = document_url;
+ LoadOrCreateGroup(manifest_url);
+ return;
+ }
+
+ // TODO(michaeln): If there was a manifest URL, the user agent may report
+ // to the user that it was ignored, to aid in application development.
+ FinishCacheSelection(NULL, NULL);
+}
+
+void AppCacheHost::SelectCacheForWorker(int parent_process_id,
+ int parent_host_id) {
+ DCHECK(pending_start_update_callback_.is_null() &&
+ pending_swap_cache_callback_.is_null() &&
+ pending_get_status_callback_.is_null() &&
+ !is_selection_pending());
+
+ parent_process_id_ = parent_process_id;
+ parent_host_id_ = parent_host_id;
+ FinishCacheSelection(NULL, NULL);
+}
+
+void AppCacheHost::SelectCacheForSharedWorker(int64 appcache_id) {
+ DCHECK(pending_start_update_callback_.is_null() &&
+ pending_swap_cache_callback_.is_null() &&
+ pending_get_status_callback_.is_null() &&
+ !is_selection_pending());
+
+ if (appcache_id != kAppCacheNoCacheId) {
+ LoadSelectedCache(appcache_id);
+ return;
+ }
+ FinishCacheSelection(NULL, NULL);
+}
+
+// TODO(michaeln): change method name to MarkEntryAsForeign for consistency
+void AppCacheHost::MarkAsForeignEntry(const GURL& document_url,
+ int64 cache_document_was_loaded_from) {
+ // The document url is not the resource url in the fallback case.
+ storage()->MarkEntryAsForeign(
+ main_resource_was_namespace_entry_ ? namespace_entry_url_ : document_url,
+ cache_document_was_loaded_from);
+ SelectCache(document_url, kAppCacheNoCacheId, GURL());
+}
+
+void AppCacheHost::GetStatusWithCallback(const GetStatusCallback& callback,
+ void* callback_param) {
+ DCHECK(pending_start_update_callback_.is_null() &&
+ pending_swap_cache_callback_.is_null() &&
+ pending_get_status_callback_.is_null());
+
+ pending_get_status_callback_ = callback;
+ pending_callback_param_ = callback_param;
+ if (is_selection_pending())
+ return;
+
+ DoPendingGetStatus();
+}
+
+void AppCacheHost::DoPendingGetStatus() {
+ DCHECK_EQ(false, pending_get_status_callback_.is_null());
+
+ pending_get_status_callback_.Run(GetStatus(), pending_callback_param_);
+ pending_get_status_callback_.Reset();
+ pending_callback_param_ = NULL;
+}
+
+void AppCacheHost::StartUpdateWithCallback(const StartUpdateCallback& callback,
+ void* callback_param) {
+ DCHECK(pending_start_update_callback_.is_null() &&
+ pending_swap_cache_callback_.is_null() &&
+ pending_get_status_callback_.is_null());
+
+ pending_start_update_callback_ = callback;
+ pending_callback_param_ = callback_param;
+ if (is_selection_pending())
+ return;
+
+ DoPendingStartUpdate();
+}
+
+void AppCacheHost::DoPendingStartUpdate() {
+ DCHECK_EQ(false, pending_start_update_callback_.is_null());
+
+ // 6.9.8 Application cache API
+ bool success = false;
+ if (associated_cache_.get() && associated_cache_->owning_group()) {
+ AppCacheGroup* group = associated_cache_->owning_group();
+ if (!group->is_obsolete() && !group->is_being_deleted()) {
+ success = true;
+ group->StartUpdate();
+ }
+ }
+
+ pending_start_update_callback_.Run(success, pending_callback_param_);
+ pending_start_update_callback_.Reset();
+ pending_callback_param_ = NULL;
+}
+
+void AppCacheHost::SwapCacheWithCallback(const SwapCacheCallback& callback,
+ void* callback_param) {
+ DCHECK(pending_start_update_callback_.is_null() &&
+ pending_swap_cache_callback_.is_null() &&
+ pending_get_status_callback_.is_null());
+
+ pending_swap_cache_callback_ = callback;
+ pending_callback_param_ = callback_param;
+ if (is_selection_pending())
+ return;
+
+ DoPendingSwapCache();
+}
+
+void AppCacheHost::DoPendingSwapCache() {
+ DCHECK_EQ(false, pending_swap_cache_callback_.is_null());
+
+ // 6.9.8 Application cache API
+ bool success = false;
+ if (associated_cache_.get() && associated_cache_->owning_group()) {
+ if (associated_cache_->owning_group()->is_obsolete()) {
+ success = true;
+ AssociateNoCache(GURL());
+ } else if (swappable_cache_.get()) {
+ DCHECK(swappable_cache_.get() ==
+ swappable_cache_->owning_group()->newest_complete_cache());
+ success = true;
+ AssociateCompleteCache(swappable_cache_.get());
+ }
+ }
+
+ pending_swap_cache_callback_.Run(success, pending_callback_param_);
+ pending_swap_cache_callback_.Reset();
+ pending_callback_param_ = NULL;
+}
+
+void AppCacheHost::SetSpawningHostId(
+ int spawning_process_id, int spawning_host_id) {
+ spawning_process_id_ = spawning_process_id;
+ spawning_host_id_ = spawning_host_id;
+}
+
+const AppCacheHost* AppCacheHost::GetSpawningHost() const {
+ AppCacheBackendImpl* backend = service_->GetBackend(spawning_process_id_);
+ return backend ? backend->GetHost(spawning_host_id_) : NULL;
+}
+
+AppCacheHost* AppCacheHost::GetParentAppCacheHost() const {
+ DCHECK(is_for_dedicated_worker());
+ AppCacheBackendImpl* backend = service_->GetBackend(parent_process_id_);
+ return backend ? backend->GetHost(parent_host_id_) : NULL;
+}
+
+AppCacheRequestHandler* AppCacheHost::CreateRequestHandler(
+ net::URLRequest* request,
+ ResourceType::Type resource_type) {
+ if (is_for_dedicated_worker()) {
+ AppCacheHost* parent_host = GetParentAppCacheHost();
+ if (parent_host)
+ return parent_host->CreateRequestHandler(request, resource_type);
+ return NULL;
+ }
+
+ if (AppCacheRequestHandler::IsMainResourceType(resource_type)) {
+ // Store the first party origin so that it can be used later in SelectCache
+ // for checking whether the creation of the appcache is allowed.
+ first_party_url_ = request->first_party_for_cookies();
+ return new AppCacheRequestHandler(this, resource_type);
+ }
+
+ if ((associated_cache() && associated_cache()->is_complete()) ||
+ is_selection_pending()) {
+ return new AppCacheRequestHandler(this, resource_type);
+ }
+ return NULL;
+}
+
+void AppCacheHost::GetResourceList(
+ AppCacheResourceInfoVector* resource_infos) {
+ if (associated_cache_.get() && associated_cache_->is_complete())
+ associated_cache_->ToResourceInfoVector(resource_infos);
+}
+
+AppCacheStatus AppCacheHost::GetStatus() {
+ // 6.9.8 Application cache API
+ AppCache* cache = associated_cache();
+ if (!cache)
+ return APPCACHE_STATUS_UNCACHED;
+
+ // A cache without an owning group represents the cache being constructed
+ // during the application cache update process.
+ if (!cache->owning_group())
+ return APPCACHE_STATUS_DOWNLOADING;
+
+ if (cache->owning_group()->is_obsolete())
+ return APPCACHE_STATUS_OBSOLETE;
+ if (cache->owning_group()->update_status() == AppCacheGroup::CHECKING)
+ return APPCACHE_STATUS_CHECKING;
+ if (cache->owning_group()->update_status() == AppCacheGroup::DOWNLOADING)
+ return APPCACHE_STATUS_DOWNLOADING;
+ if (swappable_cache_.get())
+ return APPCACHE_STATUS_UPDATE_READY;
+ return APPCACHE_STATUS_IDLE;
+}
+
+void AppCacheHost::LoadOrCreateGroup(const GURL& manifest_url) {
+ DCHECK(manifest_url.is_valid());
+ pending_selected_manifest_url_ = manifest_url;
+ storage()->LoadOrCreateGroup(manifest_url, this);
+}
+
+void AppCacheHost::OnGroupLoaded(AppCacheGroup* group,
+ const GURL& manifest_url) {
+ DCHECK(manifest_url == pending_selected_manifest_url_);
+ pending_selected_manifest_url_ = GURL();
+ FinishCacheSelection(NULL, group);
+}
+
+void AppCacheHost::LoadSelectedCache(int64 cache_id) {
+ DCHECK(cache_id != kAppCacheNoCacheId);
+ pending_selected_cache_id_ = cache_id;
+ storage()->LoadCache(cache_id, this);
+}
+
+void AppCacheHost::OnCacheLoaded(AppCache* cache, int64 cache_id) {
+ if (cache_id == pending_main_resource_cache_id_) {
+ pending_main_resource_cache_id_ = kAppCacheNoCacheId;
+ main_resource_cache_ = cache;
+ } else if (cache_id == pending_selected_cache_id_) {
+ pending_selected_cache_id_ = kAppCacheNoCacheId;
+ FinishCacheSelection(cache, NULL);
+ }
+}
+
+void AppCacheHost::FinishCacheSelection(
+ AppCache *cache, AppCacheGroup* group) {
+ DCHECK(!associated_cache());
+
+ // 6.9.6 The application cache selection algorithm
+ if (cache) {
+ // If document was loaded from an application cache, Associate document
+ // with the application cache from which it was loaded. Invoke the
+ // application cache update process for that cache and with the browsing
+ // context being navigated.
+ DCHECK(cache->owning_group());
+ DCHECK(new_master_entry_url_.is_empty());
+ DCHECK_EQ(cache->owning_group()->manifest_url(), preferred_manifest_url_);
+ AppCacheGroup* owing_group = cache->owning_group();
+ const char* kFormatString =
+ "Document was loaded from Application Cache with manifest %s";
+ frontend_->OnLogMessage(
+ host_id_, APPCACHE_LOG_INFO,
+ base::StringPrintf(
+ kFormatString, owing_group->manifest_url().spec().c_str()));
+ AssociateCompleteCache(cache);
+ if (!owing_group->is_obsolete() && !owing_group->is_being_deleted()) {
+ owing_group->StartUpdateWithHost(this);
+ ObserveGroupBeingUpdated(owing_group);
+ }
+ } else if (group && !group->is_being_deleted()) {
+ // If document was loaded using HTTP GET or equivalent, and, there is a
+ // manifest URL, and manifest URL has the same origin as document.
+ // Invoke the application cache update process for manifest URL, with
+ // the browsing context being navigated, and with document and the
+ // resource from which document was loaded as the new master resourse.
+ DCHECK(!group->is_obsolete());
+ DCHECK(new_master_entry_url_.is_valid());
+ DCHECK_EQ(group->manifest_url(), preferred_manifest_url_);
+ const char* kFormatString = group->HasCache() ?
+ "Adding master entry to Application Cache with manifest %s" :
+ "Creating Application Cache with manifest %s";
+ frontend_->OnLogMessage(
+ host_id_, APPCACHE_LOG_INFO,
+ base::StringPrintf(kFormatString,
+ group->manifest_url().spec().c_str()));
+ // The UpdateJob may produce one for us later.
+ AssociateNoCache(preferred_manifest_url_);
+ group->StartUpdateWithNewMasterEntry(this, new_master_entry_url_);
+ ObserveGroupBeingUpdated(group);
+ } else {
+ // Otherwise, the Document is not associated with any application cache.
+ new_master_entry_url_ = GURL();
+ AssociateNoCache(GURL());
+ }
+
+ // Respond to pending callbacks now that we have a selection.
+ if (!pending_get_status_callback_.is_null())
+ DoPendingGetStatus();
+ else if (!pending_start_update_callback_.is_null())
+ DoPendingStartUpdate();
+ else if (!pending_swap_cache_callback_.is_null())
+ DoPendingSwapCache();
+
+ FOR_EACH_OBSERVER(Observer, observers_, OnCacheSelectionComplete(this));
+}
+
+void AppCacheHost::OnServiceReinitialized(
+ AppCacheStorageReference* old_storage_ref) {
+ // We continue to use the disabled instance, but arrange for its
+ // deletion when its no longer needed.
+ if (old_storage_ref->storage() == storage())
+ disabled_storage_reference_ = old_storage_ref;
+}
+
+void AppCacheHost::ObserveGroupBeingUpdated(AppCacheGroup* group) {
+ DCHECK(!group_being_updated_.get());
+ group_being_updated_ = group;
+ newest_cache_of_group_being_updated_ = group->newest_complete_cache();
+ group->AddUpdateObserver(this);
+}
+
+void AppCacheHost::OnUpdateComplete(AppCacheGroup* group) {
+ DCHECK_EQ(group, group_being_updated_);
+ group->RemoveUpdateObserver(this);
+
+ // Add a reference to the newest complete cache.
+ SetSwappableCache(group);
+
+ group_being_updated_ = NULL;
+ newest_cache_of_group_being_updated_ = NULL;
+
+ if (associated_cache_info_pending_ && associated_cache_.get() &&
+ associated_cache_->is_complete()) {
+ AppCacheInfo info;
+ FillCacheInfo(
+ associated_cache_.get(), preferred_manifest_url_, GetStatus(), &info);
+ associated_cache_info_pending_ = false;
+ frontend_->OnCacheSelected(host_id_, info);
+ }
+}
+
+void AppCacheHost::SetSwappableCache(AppCacheGroup* group) {
+ if (!group) {
+ swappable_cache_ = NULL;
+ } else {
+ AppCache* new_cache = group->newest_complete_cache();
+ if (new_cache != associated_cache_.get())
+ swappable_cache_ = new_cache;
+ else
+ swappable_cache_ = NULL;
+ }
+}
+
+void AppCacheHost::LoadMainResourceCache(int64 cache_id) {
+ DCHECK(cache_id != kAppCacheNoCacheId);
+ if (pending_main_resource_cache_id_ == cache_id ||
+ (main_resource_cache_.get() &&
+ main_resource_cache_->cache_id() == cache_id)) {
+ return;
+ }
+ pending_main_resource_cache_id_ = cache_id;
+ storage()->LoadCache(cache_id, this);
+}
+
+void AppCacheHost::NotifyMainResourceIsNamespaceEntry(
+ const GURL& namespace_entry_url) {
+ main_resource_was_namespace_entry_ = true;
+ namespace_entry_url_ = namespace_entry_url;
+}
+
+void AppCacheHost::NotifyMainResourceBlocked(const GURL& manifest_url) {
+ main_resource_blocked_ = true;
+ blocked_manifest_url_ = manifest_url;
+}
+
+void AppCacheHost::PrepareForTransfer() {
+ // This can only happen prior to the document having been loaded.
+ DCHECK(!associated_cache());
+ DCHECK(!is_selection_pending());
+ DCHECK(!group_being_updated_);
+ host_id_ = kAppCacheNoHostId;
+ frontend_ = NULL;
+}
+
+void AppCacheHost::CompleteTransfer(int host_id, AppCacheFrontend* frontend) {
+ host_id_ = host_id;
+ frontend_ = frontend;
+}
+
+void AppCacheHost::AssociateNoCache(const GURL& manifest_url) {
+ // manifest url can be empty.
+ AssociateCacheHelper(NULL, manifest_url);
+}
+
+void AppCacheHost::AssociateIncompleteCache(AppCache* cache,
+ const GURL& manifest_url) {
+ DCHECK(cache && !cache->is_complete());
+ DCHECK(!manifest_url.is_empty());
+ AssociateCacheHelper(cache, manifest_url);
+}
+
+void AppCacheHost::AssociateCompleteCache(AppCache* cache) {
+ DCHECK(cache && cache->is_complete());
+ AssociateCacheHelper(cache, cache->owning_group()->manifest_url());
+}
+
+void AppCacheHost::AssociateCacheHelper(AppCache* cache,
+ const GURL& manifest_url) {
+ if (associated_cache_.get()) {
+ associated_cache_->UnassociateHost(this);
+ }
+
+ associated_cache_ = cache;
+ SetSwappableCache(cache ? cache->owning_group() : NULL);
+ associated_cache_info_pending_ = cache && !cache->is_complete();
+ AppCacheInfo info;
+ if (cache)
+ cache->AssociateHost(this);
+
+ FillCacheInfo(cache, manifest_url, GetStatus(), &info);
+ frontend_->OnCacheSelected(host_id_, info);
+}
+
+} // namespace content
diff --git a/content/browser/appcache/appcache_host.h b/content/browser/appcache/appcache_host.h
new file mode 100644
index 0000000..cef737e
--- /dev/null
+++ b/content/browser/appcache/appcache_host.h
@@ -0,0 +1,338 @@
+// Copyright (c) 2011 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.
+
+#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_HOST_H_
+#define CONTENT_BROWSER_APPCACHE_APPCACHE_HOST_H_
+
+#include "base/callback.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+#include "content/browser/appcache/appcache_group.h"
+#include "content/browser/appcache/appcache_service_impl.h"
+#include "content/browser/appcache/appcache_storage.h"
+#include "content/common/appcache_interfaces.h"
+#include "content/common/content_export.h"
+#include "url/gurl.h"
+#include "webkit/common/resource_type.h"
+
+namespace net {
+class URLRequest;
+} // namespace net
+
+namespace content {
+FORWARD_DECLARE_TEST(AppCacheGroupTest, CleanupUnusedGroup);
+FORWARD_DECLARE_TEST(AppCacheGroupTest, QueueUpdate);
+FORWARD_DECLARE_TEST(AppCacheHostTest, Basic);
+FORWARD_DECLARE_TEST(AppCacheHostTest, SelectNoCache);
+FORWARD_DECLARE_TEST(AppCacheHostTest, ForeignEntry);
+FORWARD_DECLARE_TEST(AppCacheHostTest, FailedCacheLoad);
+FORWARD_DECLARE_TEST(AppCacheHostTest, FailedGroupLoad);
+FORWARD_DECLARE_TEST(AppCacheHostTest, SetSwappableCache);
+FORWARD_DECLARE_TEST(AppCacheHostTest, ForDedicatedWorker);
+FORWARD_DECLARE_TEST(AppCacheHostTest, SelectCacheAllowed);
+FORWARD_DECLARE_TEST(AppCacheHostTest, SelectCacheBlocked);
+FORWARD_DECLARE_TEST(AppCacheTest, CleanupUnusedCache);
+class AppCacheTest;
+class AppCacheHostTest;
+class AppCacheGroupTest;
+class AppCacheStorageImplTest;
+class AppCacheRequestHandlerTest;
+class AppCacheUpdateJobTest;
+}
+
+namespace content {
+
+class AppCache;
+class AppCacheFrontend;
+class AppCacheRequestHandler;
+
+typedef base::Callback<void(AppCacheStatus, void*)> GetStatusCallback;
+typedef base::Callback<void(bool, void*)> StartUpdateCallback;
+typedef base::Callback<void(bool, void*)> SwapCacheCallback;
+
+// Server-side representation of an application cache host.
+class CONTENT_EXPORT AppCacheHost
+ : public AppCacheStorage::Delegate,
+ public AppCacheGroup::UpdateObserver,
+ public AppCacheServiceImpl::Observer {
+ public:
+
+ class CONTENT_EXPORT Observer {
+ public:
+ // Called just after the cache selection algorithm completes.
+ virtual void OnCacheSelectionComplete(AppCacheHost* host) = 0;
+
+ // Called just prior to the instance being deleted.
+ virtual void OnDestructionImminent(AppCacheHost* host) = 0;
+
+ virtual ~Observer() {}
+ };
+
+ AppCacheHost(int host_id, AppCacheFrontend* frontend,
+ AppCacheServiceImpl* service);
+ virtual ~AppCacheHost();
+
+ // Adds/removes an observer, the AppCacheHost does not take
+ // ownership of the observer.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ // Support for cache selection and scriptable method calls.
+ void SelectCache(const GURL& document_url,
+ const int64 cache_document_was_loaded_from,
+ const GURL& manifest_url);
+ void SelectCacheForWorker(int parent_process_id,
+ int parent_host_id);
+ void SelectCacheForSharedWorker(int64 appcache_id);
+ void MarkAsForeignEntry(const GURL& document_url,
+ int64 cache_document_was_loaded_from);
+ void GetStatusWithCallback(const GetStatusCallback& callback,
+ void* callback_param);
+ void StartUpdateWithCallback(const StartUpdateCallback& callback,
+ void* callback_param);
+ void SwapCacheWithCallback(const SwapCacheCallback& callback,
+ void* callback_param);
+
+ // Called prior to the main resource load. When the system contains multiple
+ // candidates for a main resource load, the appcache preferred by the host
+ // that created this host is used to break ties.
+ void SetSpawningHostId(int spawning_process_id, int spawning_host_id);
+
+ // May return NULL if the spawning host context has been closed, or if a
+ // spawning host context was never identified.
+ const AppCacheHost* GetSpawningHost() const;
+
+ const GURL& preferred_manifest_url() const {
+ return preferred_manifest_url_;
+ }
+ void set_preferred_manifest_url(const GURL& url) {
+ preferred_manifest_url_ = url;
+ }
+
+ // Support for loading resources out of the appcache.
+ // May return NULL if the request isn't subject to retrieval from an appache.
+ AppCacheRequestHandler* CreateRequestHandler(
+ net::URLRequest* request, ResourceType::Type resource_type);
+
+ // Support for devtools inspecting appcache resources.
+ void GetResourceList(std::vector<AppCacheResourceInfo>* resource_infos);
+
+ // Breaks any existing association between this host and a cache.
+ // 'manifest_url' is sent to DevTools as the manifest url that could have
+ // been associated before or could be associated later with this host.
+ // Associations are broken either thru the cache selection algorithm
+ // implemented in this class, or by the update algorithm (see
+ // AppCacheUpdateJob).
+ void AssociateNoCache(const GURL& manifest_url);
+
+ // Establishes an association between this host and an incomplete cache.
+ // 'manifest_url' is manifest url of the cache group being updated.
+ // Associations with incomplete caches are established by the update algorithm
+ // (see AppCacheUpdateJob).
+ void AssociateIncompleteCache(AppCache* cache, const GURL& manifest_url);
+
+ // Establishes an association between this host and a complete cache.
+ // Associations with complete caches are established either thru the cache
+ // selection algorithm implemented (in this class), or by the update algorithm
+ // (see AppCacheUpdateJob).
+ void AssociateCompleteCache(AppCache* cache);
+
+ // Adds a reference to the newest complete cache in a group, unless it's the
+ // same as the cache that is currently associated with the host.
+ void SetSwappableCache(AppCacheGroup* group);
+
+ // Used to ensure that a loaded appcache survives a frame navigation.
+ void LoadMainResourceCache(int64 cache_id);
+
+ // Used to notify the host that a namespace resource is being delivered as
+ // the main resource of the page and to provide its url.
+ void NotifyMainResourceIsNamespaceEntry(const GURL& namespace_entry_url);
+
+ // Used to notify the host that the main resource was blocked by a policy. To
+ // work properly, this method needs to by invoked prior to cache selection.
+ void NotifyMainResourceBlocked(const GURL& manifest_url);
+
+ // Used by the update job to keep track of which hosts are associated
+ // with which pending master entries.
+ const GURL& pending_master_entry_url() const {
+ return new_master_entry_url_;
+ }
+
+ int host_id() const { return host_id_; }
+ AppCacheServiceImpl* service() const { return service_; }
+ AppCacheStorage* storage() const { return storage_; }
+ AppCacheFrontend* frontend() const { return frontend_; }
+ AppCache* associated_cache() const { return associated_cache_.get(); }
+
+ bool is_selection_pending() const {
+ return pending_selected_cache_id_ != kAppCacheNoCacheId ||
+ !pending_selected_manifest_url_.is_empty();
+ }
+
+ const GURL& first_party_url() const { return first_party_url_; }
+
+ // Methods to support cross site navigations.
+ void PrepareForTransfer();
+ void CompleteTransfer(int host_id, AppCacheFrontend* frontend);
+
+ private:
+ friend class content::AppCacheHostTest;
+ friend class content::AppCacheStorageImplTest;
+ friend class content::AppCacheRequestHandlerTest;
+ friend class content::AppCacheUpdateJobTest;
+
+ AppCacheStatus GetStatus();
+ void LoadSelectedCache(int64 cache_id);
+ void LoadOrCreateGroup(const GURL& manifest_url);
+
+ // See public Associate*Host() methods above.
+ void AssociateCacheHelper(AppCache* cache, const GURL& manifest_url);
+
+ // AppCacheStorage::Delegate impl
+ virtual void OnCacheLoaded(AppCache* cache, int64 cache_id) OVERRIDE;
+ virtual void OnGroupLoaded(AppCacheGroup* group,
+ const GURL& manifest_url) OVERRIDE;
+ // AppCacheServiceImpl::Observer impl
+ virtual void OnServiceReinitialized(
+ AppCacheStorageReference* old_storage_ref) OVERRIDE;
+
+ void FinishCacheSelection(AppCache* cache, AppCacheGroup* group);
+ void DoPendingGetStatus();
+ void DoPendingStartUpdate();
+ void DoPendingSwapCache();
+
+ void ObserveGroupBeingUpdated(AppCacheGroup* group);
+
+ // AppCacheGroup::UpdateObserver methods.
+ virtual void OnUpdateComplete(AppCacheGroup* group) OVERRIDE;
+
+ // Returns true if this host is for a dedicated worker context.
+ bool is_for_dedicated_worker() const {
+ return parent_host_id_ != kAppCacheNoHostId;
+ }
+
+ // Returns the parent context's host instance. This is only valid
+ // to call when this instance is_for_dedicated_worker.
+ AppCacheHost* GetParentAppCacheHost() const;
+
+ // Identifies the corresponding appcache host in the child process.
+ int host_id_;
+
+ // Information about the host that created this one; the manifest
+ // preferred by our creator influences which cache our main resource
+ // should be loaded from.
+ int spawning_host_id_;
+ int spawning_process_id_;
+ GURL preferred_manifest_url_;
+
+ // Hosts for dedicated workers are special cased to shunt
+ // request handling off to the dedicated worker's parent.
+ // The scriptable api is not accessible in dedicated workers
+ // so the other aspects of this class are not relevant for
+ // these special case instances.
+ int parent_host_id_;
+ int parent_process_id_;
+
+ // Defined prior to refs to AppCaches and Groups because destruction
+ // order matters, the disabled_storage_reference_ must outlive those
+ // objects. See additional comments for the storage_ member.
+ scoped_refptr<AppCacheStorageReference> disabled_storage_reference_;
+
+ // The cache associated with this host, if any.
+ scoped_refptr<AppCache> associated_cache_;
+
+ // Hold a reference to the newest complete cache (if associated cache is
+ // not the newest) to keep the newest cache in existence while the app cache
+ // group is in use. The newest complete cache may have no associated hosts
+ // holding any references to it and would otherwise be deleted prematurely.
+ scoped_refptr<AppCache> swappable_cache_;
+
+ // Keep a reference to the group being updated until the update completes.
+ scoped_refptr<AppCacheGroup> group_being_updated_;
+
+ // Similarly, keep a reference to the newest cache of the group until the
+ // update completes. When adding a new master entry to a cache that is not
+ // in use in any other host, this reference keeps the cache in memory.
+ scoped_refptr<AppCache> newest_cache_of_group_being_updated_;
+
+ // Keep a reference to the cache of the main resource so it survives frame
+ // navigations.
+ scoped_refptr<AppCache> main_resource_cache_;
+ int64 pending_main_resource_cache_id_;
+
+ // Cache loading is async, if we're loading a specific cache or group
+ // for the purposes of cache selection, one or the other of these will
+ // indicate which cache or group is being loaded.
+ int64 pending_selected_cache_id_;
+ GURL pending_selected_manifest_url_;
+
+ // A new master entry to be added to the cache, may be empty.
+ GURL new_master_entry_url_;
+
+ // The frontend proxy to deliver notifications to the child process.
+ AppCacheFrontend* frontend_;
+
+ // Our central service object.
+ AppCacheServiceImpl* service_;
+
+ // And the equally central storage object, with a twist. In some error
+ // conditions the storage object gets recreated and reinitialized. The
+ // disabled_storage_reference_ (defined earlier) allows for cleanup of an
+ // instance that got disabled after we had latched onto it. In normal
+ // circumstances, disabled_storage_reference_ is expected to be NULL.
+ // When non-NULL both storage_ and disabled_storage_reference_ refer to the
+ // same instance.
+ AppCacheStorage* storage_;
+
+ // Since these are synchronous scriptable API calls in the client, there can
+ // only be one type of callback pending. Also, we have to wait until we have a
+ // cache selection prior to responding to these calls, as cache selection
+ // involves async loading of a cache or a group from storage.
+ GetStatusCallback pending_get_status_callback_;
+ StartUpdateCallback pending_start_update_callback_;
+ SwapCacheCallback pending_swap_cache_callback_;
+ void* pending_callback_param_;
+
+ // True if an intercept or fallback namespace resource was
+ // delivered as the main resource.
+ bool main_resource_was_namespace_entry_;
+ GURL namespace_entry_url_;
+
+ // True if requests for this host were blocked by a policy.
+ bool main_resource_blocked_;
+ GURL blocked_manifest_url_;
+
+ // Tells if info about associated cache is pending. Info is pending
+ // when update job has not returned success yet.
+ bool associated_cache_info_pending_;
+
+ // List of objects observing us.
+ ObserverList<Observer> observers_;
+
+ // Used to inform the QuotaManager of what origins are currently in use.
+ GURL origin_in_use_;
+
+ // First party url to be used in policy checks.
+ GURL first_party_url_;
+
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheGroupTest, CleanupUnusedGroup);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheGroupTest, QueueUpdate);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheHostTest, Basic);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheHostTest, SelectNoCache);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheHostTest, ForeignEntry);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheHostTest, FailedCacheLoad);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheHostTest, FailedGroupLoad);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheHostTest, SetSwappableCache);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheHostTest, ForDedicatedWorker);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheHostTest, SelectCacheAllowed);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheHostTest, SelectCacheBlocked);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheTest, CleanupUnusedCache);
+
+ DISALLOW_COPY_AND_ASSIGN(AppCacheHost);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_APPCACHE_APPCACHE_HOST_H_
diff --git a/content/browser/appcache/appcache_host_unittest.cc b/content/browser/appcache/appcache_host_unittest.cc
index 6b9970d..0959e4f 100644
--- a/content/browser/appcache/appcache_host_unittest.cc
+++ b/content/browser/appcache/appcache_host_unittest.cc
@@ -6,30 +6,16 @@
#include "base/bind_helpers.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
+#include "content/browser/appcache/appcache.h"
+#include "content/browser/appcache/appcache_backend_impl.h"
+#include "content/browser/appcache/appcache_group.h"
+#include "content/browser/appcache/appcache_host.h"
#include "content/browser/appcache/mock_appcache_policy.h"
#include "content/browser/appcache/mock_appcache_service.h"
#include "net/url_request/url_request.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/appcache/appcache.h"
-#include "webkit/browser/appcache/appcache_backend_impl.h"
-#include "webkit/browser/appcache/appcache_group.h"
-#include "webkit/browser/appcache/appcache_host.h"
#include "webkit/browser/quota/quota_manager.h"
-using appcache::AppCache;
-using appcache::AppCacheBackendImpl;
-using appcache::AppCacheEntry;
-using appcache::AppCacheFrontend;
-using appcache::AppCacheGroup;
-using appcache::AppCacheHost;
-using appcache::kAppCacheNoCacheId;
-using appcache::APPCACHE_ERROR_EVENT;
-using appcache::APPCACHE_STATUS_OBSOLETE;
-using appcache::APPCACHE_OBSOLETE_EVENT;
-using appcache::APPCACHE_PROGRESS_EVENT;
-using appcache::AppCacheStatus;
-using appcache::APPCACHE_STATUS_UNCACHED;
-
namespace content {
class AppCacheHostTest : public testing::Test {
@@ -50,32 +36,32 @@ class AppCacheHostTest : public testing::Test {
public:
MockFrontend()
: last_host_id_(-222), last_cache_id_(-222),
- last_status_(appcache::APPCACHE_STATUS_OBSOLETE),
- last_status_changed_(appcache::APPCACHE_STATUS_OBSOLETE),
- last_event_id_(appcache::APPCACHE_OBSOLETE_EVENT),
+ last_status_(APPCACHE_STATUS_OBSOLETE),
+ last_status_changed_(APPCACHE_STATUS_OBSOLETE),
+ last_event_id_(APPCACHE_OBSOLETE_EVENT),
content_blocked_(false) {
}
virtual void OnCacheSelected(
- int host_id, const appcache::AppCacheInfo& info) OVERRIDE {
+ int host_id, const AppCacheInfo& info) OVERRIDE {
last_host_id_ = host_id;
last_cache_id_ = info.cache_id;
last_status_ = info.status;
}
virtual void OnStatusChanged(const std::vector<int>& host_ids,
- appcache::AppCacheStatus status) OVERRIDE {
+ AppCacheStatus status) OVERRIDE {
last_status_changed_ = status;
}
virtual void OnEventRaised(const std::vector<int>& host_ids,
- appcache::AppCacheEventID event_id) OVERRIDE {
+ AppCacheEventID event_id) OVERRIDE {
last_event_id_ = event_id;
}
virtual void OnErrorEventRaised(
const std::vector<int>& host_ids,
- const appcache::AppCacheErrorDetails& details) OVERRIDE {
+ const AppCacheErrorDetails& details) OVERRIDE {
last_event_id_ = APPCACHE_ERROR_EVENT;
}
@@ -87,7 +73,7 @@ class AppCacheHostTest : public testing::Test {
}
virtual void OnLogMessage(int host_id,
- appcache::AppCacheLogLevel log_level,
+ AppCacheLogLevel log_level,
const std::string& message) OVERRIDE {
}
@@ -98,9 +84,9 @@ class AppCacheHostTest : public testing::Test {
int last_host_id_;
int64 last_cache_id_;
- appcache::AppCacheStatus last_status_;
- appcache::AppCacheStatus last_status_changed_;
- appcache::AppCacheEventID last_event_id_;
+ AppCacheStatus last_status_;
+ AppCacheStatus last_status_changed_;
+ AppCacheEventID last_event_id_;
bool content_blocked_;
};
@@ -172,9 +158,9 @@ class AppCacheHostTest : public testing::Test {
MockFrontend mock_frontend_;
// Mock callbacks we expect to receive from the 'host'
- appcache::GetStatusCallback get_status_callback_;
- appcache::StartUpdateCallback start_update_callback_;
- appcache::SwapCacheCallback swap_cache_callback_;
+ content::GetStatusCallback get_status_callback_;
+ content::StartUpdateCallback start_update_callback_;
+ content::SwapCacheCallback swap_cache_callback_;
AppCacheStatus last_status_result_;
bool last_swap_result_;
@@ -387,11 +373,11 @@ TEST_F(AppCacheHostTest, SetSwappableCache) {
host.AssociateCompleteCache(cache1);
EXPECT_FALSE(host.swappable_cache_.get()); // was same as associated cache
- EXPECT_EQ(appcache::APPCACHE_STATUS_IDLE, host.GetStatus());
+ EXPECT_EQ(APPCACHE_STATUS_IDLE, host.GetStatus());
// verify OnCacheSelected was called
EXPECT_EQ(host.host_id(), mock_frontend_.last_host_id_);
EXPECT_EQ(cache1->cache_id(), mock_frontend_.last_cache_id_);
- EXPECT_EQ(appcache::APPCACHE_STATUS_IDLE, mock_frontend_.last_status_);
+ EXPECT_EQ(APPCACHE_STATUS_IDLE, mock_frontend_.last_status_);
AppCache* cache2 = new AppCache(service_.storage(), 222);
cache2->set_complete(true);
diff --git a/content/browser/appcache/appcache_interceptor.cc b/content/browser/appcache/appcache_interceptor.cc
index ac7dbc3..76c5c0d 100644
--- a/content/browser/appcache/appcache_interceptor.cc
+++ b/content/browser/appcache/appcache_interceptor.cc
@@ -4,19 +4,12 @@
#include "content/browser/appcache/appcache_interceptor.h"
-#include "webkit/browser/appcache/appcache_backend_impl.h"
-#include "webkit/browser/appcache/appcache_host.h"
-#include "webkit/browser/appcache/appcache_request_handler.h"
-#include "webkit/browser/appcache/appcache_service_impl.h"
-#include "webkit/browser/appcache/appcache_url_request_job.h"
-#include "webkit/common/appcache/appcache_interfaces.h"
-
-using appcache::AppCacheBackendImpl;
-using appcache::AppCacheHost;
-using appcache::AppCacheRequestHandler;
-using appcache::AppCacheServiceImpl;
-using appcache::kAppCacheNoCacheId;
-using appcache::kAppCacheNoHostId;
+#include "content/browser/appcache/appcache_backend_impl.h"
+#include "content/browser/appcache/appcache_host.h"
+#include "content/browser/appcache/appcache_request_handler.h"
+#include "content/browser/appcache/appcache_service_impl.h"
+#include "content/browser/appcache/appcache_url_request_job.h"
+#include "content/common/appcache_interfaces.h"
namespace content {
diff --git a/content/browser/appcache/appcache_interceptor.h b/content/browser/appcache/appcache_interceptor.h
index d8da181..4f86a89 100644
--- a/content/browser/appcache/appcache_interceptor.h
+++ b/content/browser/appcache/appcache_interceptor.h
@@ -11,7 +11,7 @@
#include "url/gurl.h"
#include "webkit/common/resource_type.h"
-namespace appcache {
+namespace content {
class AppCacheRequestHandler;
class AppCacheServiceImpl;
}
@@ -31,7 +31,7 @@ class CONTENT_EXPORT AppCacheInterceptor
// Must be called to make a request eligible for retrieval from an appcache.
static void SetExtraRequestInfo(net::URLRequest* request,
- appcache::AppCacheServiceImpl* service,
+ AppCacheServiceImpl* service,
int process_id,
int host_id,
ResourceType::Type resource_type);
@@ -71,8 +71,8 @@ class CONTENT_EXPORT AppCacheInterceptor
virtual ~AppCacheInterceptor();
static void SetHandler(net::URLRequest* request,
- appcache::AppCacheRequestHandler* handler);
- static appcache::AppCacheRequestHandler* GetHandler(net::URLRequest* request);
+ AppCacheRequestHandler* handler);
+ static AppCacheRequestHandler* GetHandler(net::URLRequest* request);
DISALLOW_COPY_AND_ASSIGN(AppCacheInterceptor);
};
diff --git a/content/browser/appcache/appcache_policy.h b/content/browser/appcache/appcache_policy.h
new file mode 100644
index 0000000..e7ef6f1
--- /dev/null
+++ b/content/browser/appcache/appcache_policy.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2011 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.
+
+#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_POLICY_H_
+#define CONTENT_BROWSER_APPCACHE_APPCACHE_POLICY_H_
+
+class GURL;
+
+namespace content {
+
+class AppCachePolicy {
+ public:
+ AppCachePolicy() {}
+
+ // Called prior to loading a main resource from the appache.
+ // Returns true if allowed. This is expected to return immediately
+ // without any user prompt.
+ virtual bool CanLoadAppCache(const GURL& manifest_url,
+ const GURL& first_party) = 0;
+
+ // Called prior to creating a new appcache. Returns true if allowed.
+ virtual bool CanCreateAppCache(const GURL& manifest_url,
+ const GURL& first_party) = 0;
+
+ protected:
+ ~AppCachePolicy() {}
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_APPCACHE_APPCACHE_POLICY_H_
diff --git a/content/browser/appcache/appcache_quota_client.cc b/content/browser/appcache/appcache_quota_client.cc
new file mode 100644
index 0000000..2e8c2ab
--- /dev/null
+++ b/content/browser/appcache/appcache_quota_client.cc
@@ -0,0 +1,253 @@
+// Copyright (c) 2012 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 "content/browser/appcache/appcache_quota_client.h"
+
+#include <algorithm>
+#include <map>
+#include <set>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "content/browser/appcache/appcache_service_impl.h"
+
+using quota::QuotaClient;
+
+namespace {
+quota::QuotaStatusCode NetErrorCodeToQuotaStatus(int code) {
+ if (code == net::OK)
+ return quota::kQuotaStatusOk;
+ else if (code == net::ERR_ABORTED)
+ return quota::kQuotaErrorAbort;
+ else
+ return quota::kQuotaStatusUnknown;
+}
+
+void RunFront(content::AppCacheQuotaClient::RequestQueue* queue) {
+ base::Closure request = queue->front();
+ queue->pop_front();
+ request.Run();
+}
+} // namespace
+
+namespace content {
+
+AppCacheQuotaClient::AppCacheQuotaClient(AppCacheServiceImpl* service)
+ : service_(service),
+ appcache_is_ready_(false),
+ quota_manager_is_destroyed_(false) {
+}
+
+AppCacheQuotaClient::~AppCacheQuotaClient() {
+ DCHECK(pending_batch_requests_.empty());
+ DCHECK(pending_serial_requests_.empty());
+ DCHECK(current_delete_request_callback_.is_null());
+}
+
+QuotaClient::ID AppCacheQuotaClient::id() const {
+ return kAppcache;
+}
+
+void AppCacheQuotaClient::OnQuotaManagerDestroyed() {
+ DeletePendingRequests();
+ if (!current_delete_request_callback_.is_null()) {
+ current_delete_request_callback_.Reset();
+ GetServiceDeleteCallback()->Cancel();
+ }
+
+ quota_manager_is_destroyed_ = true;
+ if (!service_)
+ delete this;
+}
+
+void AppCacheQuotaClient::GetOriginUsage(
+ const GURL& origin,
+ quota::StorageType type,
+ const GetUsageCallback& callback) {
+ DCHECK(!callback.is_null());
+ DCHECK(!quota_manager_is_destroyed_);
+
+ if (!service_) {
+ callback.Run(0);
+ return;
+ }
+
+ if (!appcache_is_ready_) {
+ pending_batch_requests_.push_back(
+ base::Bind(&AppCacheQuotaClient::GetOriginUsage,
+ base::Unretained(this), origin, type, callback));
+ return;
+ }
+
+ if (type != quota::kStorageTypeTemporary) {
+ callback.Run(0);
+ return;
+ }
+
+ const AppCacheStorage::UsageMap* map = GetUsageMap();
+ AppCacheStorage::UsageMap::const_iterator found = map->find(origin);
+ if (found == map->end()) {
+ callback.Run(0);
+ return;
+ }
+ callback.Run(found->second);
+}
+
+void AppCacheQuotaClient::GetOriginsForType(
+ quota::StorageType type,
+ const GetOriginsCallback& callback) {
+ GetOriginsHelper(type, std::string(), callback);
+}
+
+void AppCacheQuotaClient::GetOriginsForHost(
+ quota::StorageType type,
+ const std::string& host,
+ const GetOriginsCallback& callback) {
+ DCHECK(!callback.is_null());
+ if (host.empty()) {
+ callback.Run(std::set<GURL>());
+ return;
+ }
+ GetOriginsHelper(type, host, callback);
+}
+
+void AppCacheQuotaClient::DeleteOriginData(const GURL& origin,
+ quota::StorageType type,
+ const DeletionCallback& callback) {
+ DCHECK(!quota_manager_is_destroyed_);
+
+ if (!service_) {
+ callback.Run(quota::kQuotaErrorAbort);
+ return;
+ }
+
+ if (!appcache_is_ready_ || !current_delete_request_callback_.is_null()) {
+ pending_serial_requests_.push_back(
+ base::Bind(&AppCacheQuotaClient::DeleteOriginData,
+ base::Unretained(this), origin, type, callback));
+ return;
+ }
+
+ current_delete_request_callback_ = callback;
+ if (type != quota::kStorageTypeTemporary) {
+ DidDeleteAppCachesForOrigin(net::OK);
+ return;
+ }
+
+ service_->DeleteAppCachesForOrigin(
+ origin, GetServiceDeleteCallback()->callback());
+}
+
+bool AppCacheQuotaClient::DoesSupport(quota::StorageType type) const {
+ return type == quota::kStorageTypeTemporary;
+}
+
+void AppCacheQuotaClient::DidDeleteAppCachesForOrigin(int rv) {
+ DCHECK(service_);
+ if (quota_manager_is_destroyed_)
+ return;
+
+ // Finish the request by calling our callers callback.
+ current_delete_request_callback_.Run(NetErrorCodeToQuotaStatus(rv));
+ current_delete_request_callback_.Reset();
+ if (pending_serial_requests_.empty())
+ return;
+
+ // Start the next in the queue.
+ RunFront(&pending_serial_requests_);
+}
+
+void AppCacheQuotaClient::GetOriginsHelper(
+ quota::StorageType type,
+ const std::string& opt_host,
+ const GetOriginsCallback& callback) {
+ DCHECK(!callback.is_null());
+ DCHECK(!quota_manager_is_destroyed_);
+
+ if (!service_) {
+ callback.Run(std::set<GURL>());
+ return;
+ }
+
+ if (!appcache_is_ready_) {
+ pending_batch_requests_.push_back(
+ base::Bind(&AppCacheQuotaClient::GetOriginsHelper,
+ base::Unretained(this), type, opt_host, callback));
+ return;
+ }
+
+ if (type != quota::kStorageTypeTemporary) {
+ callback.Run(std::set<GURL>());
+ return;
+ }
+
+ const AppCacheStorage::UsageMap* map = GetUsageMap();
+ std::set<GURL> origins;
+ for (AppCacheStorage::UsageMap::const_iterator iter = map->begin();
+ iter != map->end(); ++iter) {
+ if (opt_host.empty() || iter->first.host() == opt_host)
+ origins.insert(iter->first);
+ }
+ callback.Run(origins);
+}
+
+void AppCacheQuotaClient::ProcessPendingRequests() {
+ DCHECK(appcache_is_ready_);
+ while (!pending_batch_requests_.empty())
+ RunFront(&pending_batch_requests_);
+
+ if (!pending_serial_requests_.empty())
+ RunFront(&pending_serial_requests_);
+}
+
+void AppCacheQuotaClient::DeletePendingRequests() {
+ pending_batch_requests_.clear();
+ pending_serial_requests_.clear();
+}
+
+const AppCacheStorage::UsageMap* AppCacheQuotaClient::GetUsageMap() {
+ DCHECK(service_);
+ return service_->storage()->usage_map();
+}
+
+net::CancelableCompletionCallback*
+AppCacheQuotaClient::GetServiceDeleteCallback() {
+ // Lazily created due to CancelableCompletionCallback's threading
+ // restrictions, there is no way to detach from the thread created on.
+ if (!service_delete_callback_) {
+ service_delete_callback_.reset(
+ new net::CancelableCompletionCallback(
+ base::Bind(&AppCacheQuotaClient::DidDeleteAppCachesForOrigin,
+ base::Unretained(this))));
+ }
+ return service_delete_callback_.get();
+}
+
+void AppCacheQuotaClient::NotifyAppCacheReady() {
+ // Can reoccur during reinitialization.
+ if (!appcache_is_ready_) {
+ appcache_is_ready_ = true;
+ ProcessPendingRequests();
+ }
+}
+
+void AppCacheQuotaClient::NotifyAppCacheDestroyed() {
+ service_ = NULL;
+ while (!pending_batch_requests_.empty())
+ RunFront(&pending_batch_requests_);
+
+ while (!pending_serial_requests_.empty())
+ RunFront(&pending_serial_requests_);
+
+ if (!current_delete_request_callback_.is_null()) {
+ current_delete_request_callback_.Run(quota::kQuotaErrorAbort);
+ current_delete_request_callback_.Reset();
+ GetServiceDeleteCallback()->Cancel();
+ }
+
+ if (quota_manager_is_destroyed_)
+ delete this;
+}
+
+} // namespace content
diff --git a/content/browser/appcache/appcache_quota_client.h b/content/browser/appcache/appcache_quota_client.h
new file mode 100644
index 0000000..e0b7aac
--- /dev/null
+++ b/content/browser/appcache/appcache_quota_client.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2012 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.
+
+#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_QUOTA_CLIENT_H_
+#define CONTENT_BROWSER_APPCACHE_APPCACHE_QUOTA_CLIENT_H_
+
+#include <deque>
+#include <map>
+#include <string>
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/appcache/appcache_storage.h"
+#include "content/common/content_export.h"
+#include "net/base/completion_callback.h"
+#include "webkit/browser/quota/quota_client.h"
+#include "webkit/browser/quota/quota_task.h"
+#include "webkit/common/quota/quota_types.h"
+
+namespace content {
+class AppCacheQuotaClientTest;
+}
+
+namespace content {
+
+class AppCacheServiceImpl;
+class AppCacheStorageImpl;
+class AppCacheQuotaClientTest;
+
+// A QuotaClient implementation to integrate the appcache service
+// with the quota management system. The QuotaClient interface is
+// used on the IO thread by the quota manager. This class deletes
+// itself when both the quota manager and the appcache service have
+// been destroyed.
+class AppCacheQuotaClient : public quota::QuotaClient {
+ public:
+ typedef std::deque<base::Closure> RequestQueue;
+
+ virtual ~AppCacheQuotaClient();
+
+ // QuotaClient method overrides
+ virtual ID id() const OVERRIDE;
+ virtual void OnQuotaManagerDestroyed() OVERRIDE;
+ virtual void GetOriginUsage(const GURL& origin,
+ quota::StorageType type,
+ const GetUsageCallback& callback) OVERRIDE;
+ virtual void GetOriginsForType(quota::StorageType type,
+ const GetOriginsCallback& callback) OVERRIDE;
+ virtual void GetOriginsForHost(quota::StorageType type,
+ const std::string& host,
+ const GetOriginsCallback& callback) OVERRIDE;
+ virtual void DeleteOriginData(const GURL& origin,
+ quota::StorageType type,
+ const DeletionCallback& callback) OVERRIDE;
+ virtual bool DoesSupport(quota::StorageType type) const OVERRIDE;
+
+ private:
+ friend class content::AppCacheQuotaClientTest;
+ friend class AppCacheServiceImpl; // for NotifyAppCacheIsDestroyed
+ friend class AppCacheStorageImpl; // for NotifyAppCacheIsReady
+
+ CONTENT_EXPORT
+ explicit AppCacheQuotaClient(AppCacheServiceImpl* service);
+
+ void DidDeleteAppCachesForOrigin(int rv);
+ void GetOriginsHelper(quota::StorageType type,
+ const std::string& opt_host,
+ const GetOriginsCallback& callback);
+ void ProcessPendingRequests();
+ void DeletePendingRequests();
+ const AppCacheStorage::UsageMap* GetUsageMap();
+ net::CancelableCompletionCallback* GetServiceDeleteCallback();
+
+ // For use by appcache internals during initialization and shutdown.
+ CONTENT_EXPORT void NotifyAppCacheReady();
+ CONTENT_EXPORT void NotifyAppCacheDestroyed();
+
+ // Prior to appcache service being ready, we have to queue
+ // up reqeusts and defer acting on them until we're ready.
+ RequestQueue pending_batch_requests_;
+ RequestQueue pending_serial_requests_;
+
+ // And once it's ready, we can only handle one delete request at a time,
+ // so we queue up additional requests while one is in already in progress.
+ DeletionCallback current_delete_request_callback_;
+ scoped_ptr<net::CancelableCompletionCallback> service_delete_callback_;
+
+ AppCacheServiceImpl* service_;
+ bool appcache_is_ready_;
+ bool quota_manager_is_destroyed_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppCacheQuotaClient);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_APPCACHE_APPCACHE_QUOTA_CLIENT_H_
diff --git a/content/browser/appcache/appcache_quota_client_unittest.cc b/content/browser/appcache/appcache_quota_client_unittest.cc
index 3a32e95..88346ee 100644
--- a/content/browser/appcache/appcache_quota_client_unittest.cc
+++ b/content/browser/appcache/appcache_quota_client_unittest.cc
@@ -8,12 +8,10 @@
#include "base/bind.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/run_loop.h"
+#include "content/browser/appcache/appcache_quota_client.h"
#include "content/browser/appcache/mock_appcache_service.h"
#include "net/base/net_errors.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/appcache/appcache_quota_client.h"
-
-using appcache::AppCacheQuotaClient;
namespace content {
diff --git a/content/browser/appcache/appcache_request_handler.cc b/content/browser/appcache/appcache_request_handler.cc
new file mode 100644
index 0000000..2db7a52
--- /dev/null
+++ b/content/browser/appcache/appcache_request_handler.cc
@@ -0,0 +1,402 @@
+// Copyright (c) 2011 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 "content/browser/appcache/appcache_request_handler.h"
+
+#include "content/browser/appcache/appcache.h"
+#include "content/browser/appcache/appcache_backend_impl.h"
+#include "content/browser/appcache/appcache_policy.h"
+#include "content/browser/appcache/appcache_url_request_job.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_job.h"
+
+namespace content {
+
+AppCacheRequestHandler::AppCacheRequestHandler(
+ AppCacheHost* host, ResourceType::Type resource_type)
+ : host_(host), resource_type_(resource_type),
+ is_waiting_for_cache_selection_(false), found_group_id_(0),
+ found_cache_id_(0), found_network_namespace_(false),
+ cache_entry_not_found_(false), maybe_load_resource_executed_(false) {
+ DCHECK(host_);
+ host_->AddObserver(this);
+}
+
+AppCacheRequestHandler::~AppCacheRequestHandler() {
+ if (host_) {
+ storage()->CancelDelegateCallbacks(this);
+ host_->RemoveObserver(this);
+ }
+}
+
+AppCacheStorage* AppCacheRequestHandler::storage() const {
+ DCHECK(host_);
+ return host_->storage();
+}
+
+AppCacheURLRequestJob* AppCacheRequestHandler::MaybeLoadResource(
+ net::URLRequest* request, net::NetworkDelegate* network_delegate) {
+ maybe_load_resource_executed_ = true;
+ if (!host_ || !IsSchemeAndMethodSupportedForAppCache(request) ||
+ cache_entry_not_found_)
+ return NULL;
+
+ // This method can get called multiple times over the life
+ // of a request. The case we detect here is having scheduled
+ // delivery of a "network response" using a job setup on an
+ // earlier call thru this method. To send the request thru
+ // to the network involves restarting the request altogether,
+ // which will call thru to our interception layer again.
+ // This time thru, we return NULL so the request hits the wire.
+ if (job_.get()) {
+ DCHECK(job_->is_delivering_network_response() ||
+ job_->cache_entry_not_found());
+ if (job_->cache_entry_not_found())
+ cache_entry_not_found_ = true;
+ job_ = NULL;
+ storage()->CancelDelegateCallbacks(this);
+ return NULL;
+ }
+
+ // Clear out our 'found' fields since we're starting a request for a
+ // new resource, any values in those fields are no longer valid.
+ found_entry_ = AppCacheEntry();
+ found_fallback_entry_ = AppCacheEntry();
+ found_cache_id_ = kAppCacheNoCacheId;
+ found_manifest_url_ = GURL();
+ found_network_namespace_ = false;
+
+ if (is_main_resource())
+ MaybeLoadMainResource(request, network_delegate);
+ else
+ MaybeLoadSubResource(request, network_delegate);
+
+ // If its been setup to deliver a network response, we can just delete
+ // it now and return NULL instead to achieve that since it couldn't
+ // have been started yet.
+ if (job_.get() && job_->is_delivering_network_response()) {
+ DCHECK(!job_->has_been_started());
+ job_ = NULL;
+ }
+
+ return job_.get();
+}
+
+AppCacheURLRequestJob* AppCacheRequestHandler::MaybeLoadFallbackForRedirect(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ const GURL& location) {
+ if (!host_ || !IsSchemeAndMethodSupportedForAppCache(request) ||
+ cache_entry_not_found_)
+ return NULL;
+ if (is_main_resource())
+ return NULL;
+ // TODO(vabr) This is a temporary fix (see crbug/141114). We should get rid of
+ // it once a more general solution to crbug/121325 is in place.
+ if (!maybe_load_resource_executed_)
+ return NULL;
+ if (request->url().GetOrigin() == location.GetOrigin())
+ return NULL;
+
+ DCHECK(!job_.get()); // our jobs never generate redirects
+
+ if (found_fallback_entry_.has_response_id()) {
+ // 6.9.6, step 4: If this results in a redirect to another origin,
+ // get the resource of the fallback entry.
+ job_ = new AppCacheURLRequestJob(request, network_delegate,
+ storage(), host_, is_main_resource());
+ DeliverAppCachedResponse(
+ found_fallback_entry_, found_cache_id_, found_group_id_,
+ found_manifest_url_, true, found_namespace_entry_url_);
+ } else if (!found_network_namespace_) {
+ // 6.9.6, step 6: Fail the resource load.
+ job_ = new AppCacheURLRequestJob(request, network_delegate,
+ storage(), host_, is_main_resource());
+ DeliverErrorResponse();
+ } else {
+ // 6.9.6 step 3 and 5: Fetch the resource normally.
+ }
+
+ return job_.get();
+}
+
+AppCacheURLRequestJob* AppCacheRequestHandler::MaybeLoadFallbackForResponse(
+ net::URLRequest* request, net::NetworkDelegate* network_delegate) {
+ if (!host_ || !IsSchemeAndMethodSupportedForAppCache(request) ||
+ cache_entry_not_found_)
+ return NULL;
+ if (!found_fallback_entry_.has_response_id())
+ return NULL;
+
+ if (request->status().status() == net::URLRequestStatus::CANCELED) {
+ // 6.9.6, step 4: But not if the user canceled the download.
+ return NULL;
+ }
+
+ // We don't fallback for responses that we delivered.
+ if (job_.get()) {
+ DCHECK(!job_->is_delivering_network_response());
+ return NULL;
+ }
+
+ if (request->status().is_success()) {
+ int code_major = request->GetResponseCode() / 100;
+ if (code_major !=4 && code_major != 5)
+ return NULL;
+
+ // Servers can override the fallback behavior with a response header.
+ const std::string kFallbackOverrideHeader(
+ "x-chromium-appcache-fallback-override");
+ const std::string kFallbackOverrideValue(
+ "disallow-fallback");
+ std::string header_value;
+ request->GetResponseHeaderByName(kFallbackOverrideHeader, &header_value);
+ if (header_value == kFallbackOverrideValue)
+ return NULL;
+ }
+
+ // 6.9.6, step 4: If this results in a 4xx or 5xx status code
+ // or there were network errors, get the resource of the fallback entry.
+ job_ = new AppCacheURLRequestJob(request, network_delegate,
+ storage(), host_, is_main_resource());
+ DeliverAppCachedResponse(
+ found_fallback_entry_, found_cache_id_, found_group_id_,
+ found_manifest_url_, true, found_namespace_entry_url_);
+ return job_.get();
+}
+
+void AppCacheRequestHandler::GetExtraResponseInfo(
+ int64* cache_id, GURL* manifest_url) {
+ if (job_.get() && job_->is_delivering_appcache_response()) {
+ *cache_id = job_->cache_id();
+ *manifest_url = job_->manifest_url();
+ }
+}
+
+void AppCacheRequestHandler::PrepareForCrossSiteTransfer(int old_process_id) {
+ if (!host_)
+ return;
+ AppCacheBackendImpl* backend = host_->service()->GetBackend(old_process_id);
+ host_for_cross_site_transfer_ = backend->TransferHostOut(host_->host_id());
+ DCHECK_EQ(host_, host_for_cross_site_transfer_.get());
+}
+
+void AppCacheRequestHandler::CompleteCrossSiteTransfer(
+ int new_process_id, int new_host_id) {
+ if (!host_for_cross_site_transfer_.get())
+ return;
+ DCHECK_EQ(host_, host_for_cross_site_transfer_.get());
+ AppCacheBackendImpl* backend = host_->service()->GetBackend(new_process_id);
+ backend->TransferHostIn(new_host_id, host_for_cross_site_transfer_.Pass());
+}
+
+void AppCacheRequestHandler::OnDestructionImminent(AppCacheHost* host) {
+ storage()->CancelDelegateCallbacks(this);
+ host_ = NULL; // no need to RemoveObserver, the host is being deleted
+
+ // Since the host is being deleted, we don't have to complete any job
+ // that is current running. It's destined for the bit bucket anyway.
+ if (job_.get()) {
+ job_->Kill();
+ job_ = NULL;
+ }
+}
+
+void AppCacheRequestHandler::DeliverAppCachedResponse(
+ const AppCacheEntry& entry, int64 cache_id, int64 group_id,
+ const GURL& manifest_url, bool is_fallback,
+ const GURL& namespace_entry_url) {
+ DCHECK(host_ && job_.get() && job_->is_waiting());
+ DCHECK(entry.has_response_id());
+
+ if (ResourceType::IsFrame(resource_type_) && !namespace_entry_url.is_empty())
+ host_->NotifyMainResourceIsNamespaceEntry(namespace_entry_url);
+
+ job_->DeliverAppCachedResponse(manifest_url, group_id, cache_id,
+ entry, is_fallback);
+}
+
+void AppCacheRequestHandler::DeliverErrorResponse() {
+ DCHECK(job_.get() && job_->is_waiting());
+ job_->DeliverErrorResponse();
+}
+
+void AppCacheRequestHandler::DeliverNetworkResponse() {
+ DCHECK(job_.get() && job_->is_waiting());
+ job_->DeliverNetworkResponse();
+}
+
+// Main-resource handling ----------------------------------------------
+
+void AppCacheRequestHandler::MaybeLoadMainResource(
+ net::URLRequest* request, net::NetworkDelegate* network_delegate) {
+ DCHECK(!job_.get());
+ DCHECK(host_);
+
+ const AppCacheHost* spawning_host =
+ ResourceType::IsSharedWorker(resource_type_) ?
+ host_ : host_->GetSpawningHost();
+ GURL preferred_manifest_url = spawning_host ?
+ spawning_host->preferred_manifest_url() : GURL();
+
+ // We may have to wait for our storage query to complete, but
+ // this query can also complete syncrhonously.
+ job_ = new AppCacheURLRequestJob(request, network_delegate,
+ storage(), host_, is_main_resource());
+ storage()->FindResponseForMainRequest(
+ request->url(), preferred_manifest_url, this);
+}
+
+void AppCacheRequestHandler::OnMainResponseFound(
+ const GURL& url, const AppCacheEntry& entry,
+ const GURL& namespace_entry_url, const AppCacheEntry& fallback_entry,
+ int64 cache_id, int64 group_id, const GURL& manifest_url) {
+ DCHECK(job_.get());
+ DCHECK(host_);
+ DCHECK(is_main_resource());
+ DCHECK(!entry.IsForeign());
+ DCHECK(!fallback_entry.IsForeign());
+ DCHECK(!(entry.has_response_id() && fallback_entry.has_response_id()));
+
+ if (!job_.get())
+ return;
+
+ AppCachePolicy* policy = host_->service()->appcache_policy();
+ bool was_blocked_by_policy = !manifest_url.is_empty() && policy &&
+ !policy->CanLoadAppCache(manifest_url, host_->first_party_url());
+
+ if (was_blocked_by_policy) {
+ if (ResourceType::IsFrame(resource_type_)) {
+ host_->NotifyMainResourceBlocked(manifest_url);
+ } else {
+ DCHECK(ResourceType::IsSharedWorker(resource_type_));
+ host_->frontend()->OnContentBlocked(host_->host_id(), manifest_url);
+ }
+ DeliverNetworkResponse();
+ return;
+ }
+
+ if (ResourceType::IsFrame(resource_type_) && cache_id != kAppCacheNoCacheId) {
+ // AppCacheHost loads and holds a reference to the main resource cache
+ // for two reasons, firstly to preload the cache into the working set
+ // in advance of subresource loads happening, secondly to prevent the
+ // AppCache from falling out of the working set on frame navigations.
+ host_->LoadMainResourceCache(cache_id);
+ host_->set_preferred_manifest_url(manifest_url);
+ }
+
+ // 6.11.1 Navigating across documents, steps 10 and 14.
+
+ found_entry_ = entry;
+ found_namespace_entry_url_ = namespace_entry_url;
+ found_fallback_entry_ = fallback_entry;
+ found_cache_id_ = cache_id;
+ found_group_id_ = group_id;
+ found_manifest_url_ = manifest_url;
+ found_network_namespace_ = false; // not applicable to main requests
+
+ if (found_entry_.has_response_id()) {
+ DCHECK(!found_fallback_entry_.has_response_id());
+ DeliverAppCachedResponse(
+ found_entry_, found_cache_id_, found_group_id_, found_manifest_url_,
+ false, found_namespace_entry_url_);
+ } else {
+ DeliverNetworkResponse();
+ }
+}
+
+// Sub-resource handling ----------------------------------------------
+
+void AppCacheRequestHandler::MaybeLoadSubResource(
+ net::URLRequest* request, net::NetworkDelegate* network_delegate) {
+ DCHECK(!job_.get());
+
+ if (host_->is_selection_pending()) {
+ // We have to wait until cache selection is complete and the
+ // selected cache is loaded.
+ is_waiting_for_cache_selection_ = true;
+ job_ = new AppCacheURLRequestJob(request, network_delegate,
+ storage(), host_, is_main_resource());
+ return;
+ }
+
+ if (!host_->associated_cache() ||
+ !host_->associated_cache()->is_complete() ||
+ host_->associated_cache()->owning_group()->is_being_deleted()) {
+ return;
+ }
+
+ job_ = new AppCacheURLRequestJob(request, network_delegate,
+ storage(), host_, is_main_resource());
+ ContinueMaybeLoadSubResource();
+}
+
+void AppCacheRequestHandler::ContinueMaybeLoadSubResource() {
+ // 6.9.6 Changes to the networking model
+ // If the resource is not to be fetched using the HTTP GET mechanism or
+ // equivalent ... then fetch the resource normally.
+ DCHECK(job_.get());
+ DCHECK(host_->associated_cache() && host_->associated_cache()->is_complete());
+
+ const GURL& url = job_->request()->url();
+ AppCache* cache = host_->associated_cache();
+ storage()->FindResponseForSubRequest(
+ host_->associated_cache(), url,
+ &found_entry_, &found_fallback_entry_, &found_network_namespace_);
+
+ if (found_entry_.has_response_id()) {
+ // Step 2: If there's an entry, get it instead.
+ DCHECK(!found_network_namespace_ &&
+ !found_fallback_entry_.has_response_id());
+ found_cache_id_ = cache->cache_id();
+ found_group_id_ = cache->owning_group()->group_id();
+ found_manifest_url_ = cache->owning_group()->manifest_url();
+ DeliverAppCachedResponse(
+ found_entry_, found_cache_id_, found_group_id_, found_manifest_url_,
+ false, GURL());
+ return;
+ }
+
+ if (found_fallback_entry_.has_response_id()) {
+ // Step 4: Fetch the resource normally, if this results
+ // in certain conditions, then use the fallback.
+ DCHECK(!found_network_namespace_ &&
+ !found_entry_.has_response_id());
+ found_cache_id_ = cache->cache_id();
+ found_manifest_url_ = cache->owning_group()->manifest_url();
+ DeliverNetworkResponse();
+ return;
+ }
+
+ if (found_network_namespace_) {
+ // Step 3 and 5: Fetch the resource normally.
+ DCHECK(!found_entry_.has_response_id() &&
+ !found_fallback_entry_.has_response_id());
+ DeliverNetworkResponse();
+ return;
+ }
+
+ // Step 6: Fail the resource load.
+ DeliverErrorResponse();
+}
+
+void AppCacheRequestHandler::OnCacheSelectionComplete(AppCacheHost* host) {
+ DCHECK(host == host_);
+ if (is_main_resource())
+ return;
+ if (!is_waiting_for_cache_selection_)
+ return;
+
+ is_waiting_for_cache_selection_ = false;
+
+ if (!host_->associated_cache() ||
+ !host_->associated_cache()->is_complete()) {
+ DeliverNetworkResponse();
+ return;
+ }
+
+ ContinueMaybeLoadSubResource();
+}
+
+} // namespace content
diff --git a/content/browser/appcache/appcache_request_handler.h b/content/browser/appcache/appcache_request_handler.h
new file mode 100644
index 0000000..c73e1dd
--- /dev/null
+++ b/content/browser/appcache/appcache_request_handler.h
@@ -0,0 +1,152 @@
+// Copyright (c) 2011 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.
+
+#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_REQUEST_HANDLER_H_
+#define CONTENT_BROWSER_APPCACHE_APPCACHE_REQUEST_HANDLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/supports_user_data.h"
+#include "content/browser/appcache/appcache_entry.h"
+#include "content/browser/appcache/appcache_host.h"
+#include "content/common/content_export.h"
+#include "webkit/common/resource_type.h"
+
+namespace net {
+class NetworkDelegate;
+class URLRequest;
+class URLRequestJob;
+} // namespace net
+
+namespace content {
+class AppCacheRequestHandlerTest;
+}
+
+namespace content {
+
+class AppCacheURLRequestJob;
+
+// An instance is created for each net::URLRequest. The instance survives all
+// http transactions involved in the processing of its net::URLRequest, and is
+// given the opportunity to hijack the request along the way. Callers
+// should use AppCacheHost::CreateRequestHandler to manufacture instances
+// that can retrieve resources for a particular host.
+class CONTENT_EXPORT AppCacheRequestHandler
+ : public base::SupportsUserData::Data,
+ public AppCacheHost::Observer,
+ public AppCacheStorage::Delegate {
+ public:
+ virtual ~AppCacheRequestHandler();
+
+ // These are called on each request intercept opportunity.
+ AppCacheURLRequestJob* MaybeLoadResource(
+ net::URLRequest* request, net::NetworkDelegate* network_delegate);
+ AppCacheURLRequestJob* MaybeLoadFallbackForRedirect(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ const GURL& location);
+ AppCacheURLRequestJob* MaybeLoadFallbackForResponse(
+ net::URLRequest* request, net::NetworkDelegate* network_delegate);
+
+ void GetExtraResponseInfo(int64* cache_id, GURL* manifest_url);
+
+ // Methods to support cross site navigations.
+ void PrepareForCrossSiteTransfer(int old_process_id);
+ void CompleteCrossSiteTransfer(int new_process_id, int new_host_id);
+
+ static bool IsMainResourceType(ResourceType::Type type) {
+ return ResourceType::IsFrame(type) ||
+ ResourceType::IsSharedWorker(type);
+ }
+
+ private:
+ friend class AppCacheHost;
+
+ // Callers should use AppCacheHost::CreateRequestHandler.
+ AppCacheRequestHandler(AppCacheHost* host, ResourceType::Type resource_type);
+
+ // AppCacheHost::Observer override
+ virtual void OnDestructionImminent(AppCacheHost* host) OVERRIDE;
+
+ // Helpers to instruct a waiting job with what response to
+ // deliver for the request we're handling.
+ void DeliverAppCachedResponse(const AppCacheEntry& entry, int64 cache_id,
+ int64 group_id, const GURL& manifest_url,
+ bool is_fallback,
+ const GURL& namespace_entry_url);
+ void DeliverNetworkResponse();
+ void DeliverErrorResponse();
+
+ // Helper to retrieve a pointer to the storage object.
+ AppCacheStorage* storage() const;
+
+ bool is_main_resource() const {
+ return IsMainResourceType(resource_type_);
+ }
+
+ // Main-resource loading -------------------------------------
+ // Frame and SharedWorker main resources are handled here.
+
+ void MaybeLoadMainResource(net::URLRequest* request,
+ net::NetworkDelegate* network_delegate);
+
+ // AppCacheStorage::Delegate methods
+ 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;
+
+ // Sub-resource loading -------------------------------------
+ // Dedicated worker and all manner of sub-resources are handled here.
+
+ void MaybeLoadSubResource(net::URLRequest* request,
+ net::NetworkDelegate* network_delegate);
+ void ContinueMaybeLoadSubResource();
+
+ // AppCacheHost::Observer override
+ virtual void OnCacheSelectionComplete(AppCacheHost* host) OVERRIDE;
+
+ // Data members -----------------------------------------------
+
+ // What host we're servicing a request for.
+ AppCacheHost* host_;
+
+ // Frame vs subresource vs sharedworker loads are somewhat different.
+ ResourceType::Type resource_type_;
+
+ // Subresource requests wait until after cache selection completes.
+ bool is_waiting_for_cache_selection_;
+
+ // Info about the type of response we found for delivery.
+ // These are relevant for both main and subresource requests.
+ int64 found_group_id_;
+ int64 found_cache_id_;
+ AppCacheEntry found_entry_;
+ AppCacheEntry found_fallback_entry_;
+ GURL found_namespace_entry_url_;
+ GURL found_manifest_url_;
+ bool found_network_namespace_;
+
+ // True if a cache entry this handler attempted to return was
+ // not found in the disk cache. Once set, the handler will take
+ // no action on all subsequent intercept opportunities, so the
+ // request and any redirects will be handled by the network library.
+ bool cache_entry_not_found_;
+
+ // True if this->MaybeLoadResource(...) has been called in the past.
+ bool maybe_load_resource_executed_;
+
+ // The job we use to deliver a response.
+ scoped_refptr<AppCacheURLRequestJob> job_;
+
+ // During a cross site navigation, we transfer ownership the AppcacheHost
+ // from the old processes structures over to the new structures.
+ scoped_ptr<AppCacheHost> host_for_cross_site_transfer_;
+
+ friend class content::AppCacheRequestHandlerTest;
+ DISALLOW_COPY_AND_ASSIGN(AppCacheRequestHandler);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_APPCACHE_APPCACHE_REQUEST_HANDLER_H_
diff --git a/content/browser/appcache/appcache_request_handler_unittest.cc b/content/browser/appcache/appcache_request_handler_unittest.cc
index df6fb33..e8cc8f6 100644
--- a/content/browser/appcache/appcache_request_handler_unittest.cc
+++ b/content/browser/appcache/appcache_request_handler_unittest.cc
@@ -12,6 +12,10 @@
#include "base/message_loop/message_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
+#include "content/browser/appcache/appcache.h"
+#include "content/browser/appcache/appcache_backend_impl.h"
+#include "content/browser/appcache/appcache_request_handler.h"
+#include "content/browser/appcache/appcache_url_request_job.h"
#include "content/browser/appcache/mock_appcache_policy.h"
#include "content/browser/appcache/mock_appcache_service.h"
#include "net/base/net_errors.h"
@@ -22,21 +26,6 @@
#include "net/url_request/url_request_error_job.h"
#include "net/url_request/url_request_job_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/appcache/appcache.h"
-#include "webkit/browser/appcache/appcache_backend_impl.h"
-#include "webkit/browser/appcache/appcache_request_handler.h"
-#include "webkit/browser/appcache/appcache_url_request_job.h"
-
-using appcache::AppCache;
-using appcache::AppCacheBackendImpl;
-using appcache::AppCacheEntry;
-using appcache::AppCacheFrontend;
-using appcache::AppCacheGroup;
-using appcache::AppCacheHost;
-using appcache::AppCacheInfo;
-using appcache::AppCacheRequestHandler;
-using appcache::AppCacheURLRequestJob;
-using appcache::kAppCacheNoCacheId;
namespace content {
@@ -47,17 +36,17 @@ class AppCacheRequestHandlerTest : public testing::Test {
class MockFrontend : public AppCacheFrontend {
public:
virtual void OnCacheSelected(
- int host_id, const appcache::AppCacheInfo& info) OVERRIDE {}
+ int host_id, const AppCacheInfo& info) OVERRIDE {}
virtual void OnStatusChanged(const std::vector<int>& host_ids,
- appcache::AppCacheStatus status) OVERRIDE {}
+ AppCacheStatus status) OVERRIDE {}
virtual void OnEventRaised(const std::vector<int>& host_ids,
- appcache::AppCacheEventID event_id) OVERRIDE {}
+ AppCacheEventID event_id) OVERRIDE {}
virtual void OnErrorEventRaised(
const std::vector<int>& host_ids,
- const appcache::AppCacheErrorDetails& details) OVERRIDE {}
+ const AppCacheErrorDetails& details) OVERRIDE {}
virtual void OnProgressEventRaised(const std::vector<int>& host_ids,
const GURL& url,
@@ -66,7 +55,7 @@ class AppCacheRequestHandlerTest : public testing::Test {
}
virtual void OnLogMessage(int host_id,
- appcache::AppCacheLogLevel log_level,
+ AppCacheLogLevel log_level,
const std::string& message) OVERRIDE {
}
diff --git a/content/browser/appcache/appcache_response.cc b/content/browser/appcache/appcache_response.cc
new file mode 100644
index 0000000..b1d2366
--- /dev/null
+++ b/content/browser/appcache/appcache_response.cc
@@ -0,0 +1,423 @@
+// Copyright (c) 2012 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 "content/browser/appcache/appcache_response.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/pickle.h"
+#include "base/strings/string_util.h"
+#include "content/browser/appcache/appcache_storage.h"
+#include "net/base/completion_callback.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+
+namespace content {
+
+namespace {
+
+// Disk cache entry data indices.
+enum {
+ kResponseInfoIndex,
+ kResponseContentIndex
+};
+
+// An IOBuffer that wraps a pickle's data. Ownership of the
+// pickle is transfered to the WrappedPickleIOBuffer object.
+class WrappedPickleIOBuffer : public net::WrappedIOBuffer {
+ public:
+ explicit WrappedPickleIOBuffer(const Pickle* pickle) :
+ net::WrappedIOBuffer(reinterpret_cast<const char*>(pickle->data())),
+ pickle_(pickle) {
+ DCHECK(pickle->data());
+ }
+
+ private:
+ virtual ~WrappedPickleIOBuffer() {}
+
+ scoped_ptr<const Pickle> pickle_;
+};
+
+} // anon namespace
+
+
+// AppCacheResponseInfo ----------------------------------------------
+
+AppCacheResponseInfo::AppCacheResponseInfo(
+ AppCacheStorage* storage, const GURL& manifest_url,
+ int64 response_id, net::HttpResponseInfo* http_info,
+ int64 response_data_size)
+ : manifest_url_(manifest_url), response_id_(response_id),
+ http_response_info_(http_info), response_data_size_(response_data_size),
+ storage_(storage) {
+ DCHECK(http_info);
+ DCHECK(response_id != kAppCacheNoResponseId);
+ storage_->working_set()->AddResponseInfo(this);
+}
+
+AppCacheResponseInfo::~AppCacheResponseInfo() {
+ storage_->working_set()->RemoveResponseInfo(this);
+}
+
+// HttpResponseInfoIOBuffer ------------------------------------------
+
+HttpResponseInfoIOBuffer::HttpResponseInfoIOBuffer()
+ : response_data_size(kUnkownResponseDataSize) {}
+
+HttpResponseInfoIOBuffer::HttpResponseInfoIOBuffer(net::HttpResponseInfo* info)
+ : http_info(info), response_data_size(kUnkownResponseDataSize) {}
+
+HttpResponseInfoIOBuffer::~HttpResponseInfoIOBuffer() {}
+
+// AppCacheResponseIO ----------------------------------------------
+
+AppCacheResponseIO::AppCacheResponseIO(
+ int64 response_id, int64 group_id, AppCacheDiskCacheInterface* disk_cache)
+ : response_id_(response_id),
+ group_id_(group_id),
+ disk_cache_(disk_cache),
+ entry_(NULL),
+ buffer_len_(0),
+ weak_factory_(this) {
+}
+
+AppCacheResponseIO::~AppCacheResponseIO() {
+ if (entry_)
+ entry_->Close();
+}
+
+void AppCacheResponseIO::ScheduleIOCompletionCallback(int result) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&AppCacheResponseIO::OnIOComplete,
+ weak_factory_.GetWeakPtr(), result));
+}
+
+void AppCacheResponseIO::InvokeUserCompletionCallback(int result) {
+ // Clear the user callback and buffers prior to invoking the callback
+ // so the caller can schedule additional operations in the callback.
+ buffer_ = NULL;
+ info_buffer_ = NULL;
+ net::CompletionCallback cb = callback_;
+ callback_.Reset();
+ cb.Run(result);
+}
+
+void AppCacheResponseIO::ReadRaw(int index, int offset,
+ net::IOBuffer* buf, int buf_len) {
+ DCHECK(entry_);
+ int rv = entry_->Read(
+ index, offset, buf, buf_len,
+ base::Bind(&AppCacheResponseIO::OnRawIOComplete,
+ weak_factory_.GetWeakPtr()));
+ if (rv != net::ERR_IO_PENDING)
+ ScheduleIOCompletionCallback(rv);
+}
+
+void AppCacheResponseIO::WriteRaw(int index, int offset,
+ net::IOBuffer* buf, int buf_len) {
+ DCHECK(entry_);
+ int rv = entry_->Write(
+ index, offset, buf, buf_len,
+ base::Bind(&AppCacheResponseIO::OnRawIOComplete,
+ weak_factory_.GetWeakPtr()));
+ if (rv != net::ERR_IO_PENDING)
+ ScheduleIOCompletionCallback(rv);
+}
+
+void AppCacheResponseIO::OnRawIOComplete(int result) {
+ DCHECK_NE(net::ERR_IO_PENDING, result);
+ OnIOComplete(result);
+}
+
+
+// AppCacheResponseReader ----------------------------------------------
+
+AppCacheResponseReader::AppCacheResponseReader(
+ int64 response_id, int64 group_id, AppCacheDiskCacheInterface* disk_cache)
+ : AppCacheResponseIO(response_id, group_id, disk_cache),
+ range_offset_(0),
+ range_length_(kint32max),
+ read_position_(0),
+ weak_factory_(this) {
+}
+
+AppCacheResponseReader::~AppCacheResponseReader() {
+}
+
+void AppCacheResponseReader::ReadInfo(HttpResponseInfoIOBuffer* info_buf,
+ const net::CompletionCallback& callback) {
+ DCHECK(!callback.is_null());
+ DCHECK(!IsReadPending());
+ DCHECK(info_buf);
+ DCHECK(!info_buf->http_info.get());
+ DCHECK(!buffer_.get());
+ DCHECK(!info_buffer_.get());
+
+ info_buffer_ = info_buf;
+ callback_ = callback; // cleared on completion
+ OpenEntryIfNeededAndContinue();
+}
+
+void AppCacheResponseReader::ContinueReadInfo() {
+ if (!entry_) {
+ ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
+ return;
+ }
+
+ int size = entry_->GetSize(kResponseInfoIndex);
+ if (size <= 0) {
+ ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
+ return;
+ }
+
+ buffer_ = new net::IOBuffer(size);
+ ReadRaw(kResponseInfoIndex, 0, buffer_.get(), size);
+}
+
+void AppCacheResponseReader::ReadData(net::IOBuffer* buf, int buf_len,
+ const net::CompletionCallback& callback) {
+ DCHECK(!callback.is_null());
+ DCHECK(!IsReadPending());
+ DCHECK(buf);
+ DCHECK(buf_len >= 0);
+ DCHECK(!buffer_.get());
+ DCHECK(!info_buffer_.get());
+
+ buffer_ = buf;
+ buffer_len_ = buf_len;
+ callback_ = callback; // cleared on completion
+ OpenEntryIfNeededAndContinue();
+}
+
+void AppCacheResponseReader::ContinueReadData() {
+ if (!entry_) {
+ ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
+ return;
+ }
+
+ if (read_position_ + buffer_len_ > range_length_) {
+ // TODO(michaeln): What about integer overflows?
+ DCHECK(range_length_ >= read_position_);
+ buffer_len_ = range_length_ - read_position_;
+ }
+ ReadRaw(kResponseContentIndex,
+ range_offset_ + read_position_,
+ buffer_.get(),
+ buffer_len_);
+}
+
+void AppCacheResponseReader::SetReadRange(int offset, int length) {
+ DCHECK(!IsReadPending() && !read_position_);
+ range_offset_ = offset;
+ range_length_ = length;
+}
+
+void AppCacheResponseReader::OnIOComplete(int result) {
+ if (result >= 0) {
+ if (info_buffer_.get()) {
+ // Deserialize the http info structure, ensuring we got headers.
+ Pickle pickle(buffer_->data(), result);
+ scoped_ptr<net::HttpResponseInfo> info(new net::HttpResponseInfo);
+ bool response_truncated = false;
+ if (!info->InitFromPickle(pickle, &response_truncated) ||
+ !info->headers.get()) {
+ InvokeUserCompletionCallback(net::ERR_FAILED);
+ return;
+ }
+ DCHECK(!response_truncated);
+ info_buffer_->http_info.reset(info.release());
+
+ // Also return the size of the response body
+ DCHECK(entry_);
+ info_buffer_->response_data_size =
+ entry_->GetSize(kResponseContentIndex);
+ } else {
+ read_position_ += result;
+ }
+ }
+ InvokeUserCompletionCallback(result);
+}
+
+void AppCacheResponseReader::OpenEntryIfNeededAndContinue() {
+ int rv;
+ AppCacheDiskCacheInterface::Entry** entry_ptr = NULL;
+ if (entry_) {
+ rv = net::OK;
+ } else if (!disk_cache_) {
+ rv = net::ERR_FAILED;
+ } else {
+ entry_ptr = new AppCacheDiskCacheInterface::Entry*;
+ open_callback_ =
+ base::Bind(&AppCacheResponseReader::OnOpenEntryComplete,
+ weak_factory_.GetWeakPtr(), base::Owned(entry_ptr));
+ rv = disk_cache_->OpenEntry(response_id_, entry_ptr, open_callback_);
+ }
+
+ if (rv != net::ERR_IO_PENDING)
+ OnOpenEntryComplete(entry_ptr, rv);
+}
+
+void AppCacheResponseReader::OnOpenEntryComplete(
+ AppCacheDiskCacheInterface::Entry** entry, int rv) {
+ DCHECK(info_buffer_.get() || buffer_.get());
+
+ if (!open_callback_.is_null()) {
+ if (rv == net::OK) {
+ DCHECK(entry);
+ entry_ = *entry;
+ }
+ open_callback_.Reset();
+ }
+
+ if (info_buffer_.get())
+ ContinueReadInfo();
+ else
+ ContinueReadData();
+}
+
+// AppCacheResponseWriter ----------------------------------------------
+
+AppCacheResponseWriter::AppCacheResponseWriter(
+ int64 response_id, int64 group_id, AppCacheDiskCacheInterface* disk_cache)
+ : AppCacheResponseIO(response_id, group_id, disk_cache),
+ info_size_(0),
+ write_position_(0),
+ write_amount_(0),
+ creation_phase_(INITIAL_ATTEMPT),
+ weak_factory_(this) {
+}
+
+AppCacheResponseWriter::~AppCacheResponseWriter() {
+}
+
+void AppCacheResponseWriter::WriteInfo(
+ HttpResponseInfoIOBuffer* info_buf,
+ const net::CompletionCallback& callback) {
+ DCHECK(!callback.is_null());
+ DCHECK(!IsWritePending());
+ DCHECK(info_buf);
+ DCHECK(info_buf->http_info.get());
+ DCHECK(!buffer_.get());
+ DCHECK(!info_buffer_.get());
+ DCHECK(info_buf->http_info->headers.get());
+
+ info_buffer_ = info_buf;
+ callback_ = callback; // cleared on completion
+ CreateEntryIfNeededAndContinue();
+}
+
+void AppCacheResponseWriter::ContinueWriteInfo() {
+ if (!entry_) {
+ ScheduleIOCompletionCallback(net::ERR_FAILED);
+ return;
+ }
+
+ const bool kSkipTransientHeaders = true;
+ const bool kTruncated = false;
+ Pickle* pickle = new Pickle;
+ info_buffer_->http_info->Persist(pickle, kSkipTransientHeaders, kTruncated);
+ write_amount_ = static_cast<int>(pickle->size());
+ buffer_ = new WrappedPickleIOBuffer(pickle); // takes ownership of pickle
+ WriteRaw(kResponseInfoIndex, 0, buffer_.get(), write_amount_);
+}
+
+void AppCacheResponseWriter::WriteData(
+ net::IOBuffer* buf, int buf_len, const net::CompletionCallback& callback) {
+ DCHECK(!callback.is_null());
+ DCHECK(!IsWritePending());
+ DCHECK(buf);
+ DCHECK(buf_len >= 0);
+ DCHECK(!buffer_.get());
+ DCHECK(!info_buffer_.get());
+
+ buffer_ = buf;
+ write_amount_ = buf_len;
+ callback_ = callback; // cleared on completion
+ CreateEntryIfNeededAndContinue();
+}
+
+void AppCacheResponseWriter::ContinueWriteData() {
+ if (!entry_) {
+ ScheduleIOCompletionCallback(net::ERR_FAILED);
+ return;
+ }
+ WriteRaw(
+ kResponseContentIndex, write_position_, buffer_.get(), write_amount_);
+}
+
+void AppCacheResponseWriter::OnIOComplete(int result) {
+ if (result >= 0) {
+ DCHECK(write_amount_ == result);
+ if (!info_buffer_.get())
+ write_position_ += result;
+ else
+ info_size_ = result;
+ }
+ InvokeUserCompletionCallback(result);
+}
+
+void AppCacheResponseWriter::CreateEntryIfNeededAndContinue() {
+ int rv;
+ AppCacheDiskCacheInterface::Entry** entry_ptr = NULL;
+ if (entry_) {
+ creation_phase_ = NO_ATTEMPT;
+ rv = net::OK;
+ } else if (!disk_cache_) {
+ creation_phase_ = NO_ATTEMPT;
+ rv = net::ERR_FAILED;
+ } else {
+ creation_phase_ = INITIAL_ATTEMPT;
+ entry_ptr = new AppCacheDiskCacheInterface::Entry*;
+ create_callback_ =
+ base::Bind(&AppCacheResponseWriter::OnCreateEntryComplete,
+ weak_factory_.GetWeakPtr(), base::Owned(entry_ptr));
+ rv = disk_cache_->CreateEntry(response_id_, entry_ptr, create_callback_);
+ }
+ if (rv != net::ERR_IO_PENDING)
+ OnCreateEntryComplete(entry_ptr, rv);
+}
+
+void AppCacheResponseWriter::OnCreateEntryComplete(
+ AppCacheDiskCacheInterface::Entry** entry, int rv) {
+ DCHECK(info_buffer_.get() || buffer_.get());
+
+ if (creation_phase_ == INITIAL_ATTEMPT) {
+ if (rv != net::OK) {
+ // We may try to overwrite existing entries.
+ creation_phase_ = DOOM_EXISTING;
+ rv = disk_cache_->DoomEntry(response_id_, create_callback_);
+ if (rv != net::ERR_IO_PENDING)
+ OnCreateEntryComplete(NULL, rv);
+ return;
+ }
+ } else if (creation_phase_ == DOOM_EXISTING) {
+ creation_phase_ = SECOND_ATTEMPT;
+ AppCacheDiskCacheInterface::Entry** entry_ptr =
+ new AppCacheDiskCacheInterface::Entry*;
+ create_callback_ =
+ base::Bind(&AppCacheResponseWriter::OnCreateEntryComplete,
+ weak_factory_.GetWeakPtr(), base::Owned(entry_ptr));
+ rv = disk_cache_->CreateEntry(response_id_, entry_ptr, create_callback_);
+ if (rv != net::ERR_IO_PENDING)
+ OnCreateEntryComplete(entry_ptr, rv);
+ return;
+ }
+
+ if (!create_callback_.is_null()) {
+ if (rv == net::OK)
+ entry_ = *entry;
+
+ create_callback_.Reset();
+ }
+
+ if (info_buffer_.get())
+ ContinueWriteInfo();
+ else
+ ContinueWriteData();
+}
+
+} // namespace content
diff --git a/content/browser/appcache/appcache_response.h b/content/browser/appcache/appcache_response.h
new file mode 100644
index 0000000..474b4d0
--- /dev/null
+++ b/content/browser/appcache/appcache_response.h
@@ -0,0 +1,266 @@
+// Copyright (c) 2012 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.
+
+#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_RESPONSE_H_
+#define CONTENT_BROWSER_APPCACHE_APPCACHE_RESPONSE_H_
+
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "content/common/appcache_interfaces.h"
+#include "content/common/content_export.h"
+#include "net/base/completion_callback.h"
+#include "net/http/http_response_info.h"
+#include "url/gurl.h"
+
+namespace net {
+class IOBuffer;
+}
+
+namespace content {
+class MockAppCacheStorage;
+}
+
+namespace content {
+
+class AppCacheStorage;
+
+static const int kUnkownResponseDataSize = -1;
+
+// Response info for a particular response id. Instances are tracked in
+// the working set.
+class CONTENT_EXPORT AppCacheResponseInfo
+ : public base::RefCounted<AppCacheResponseInfo> {
+ public:
+ // AppCacheResponseInfo takes ownership of the http_info.
+ AppCacheResponseInfo(AppCacheStorage* storage, const GURL& manifest_url,
+ int64 response_id, net::HttpResponseInfo* http_info,
+ int64 response_data_size);
+
+ const GURL& manifest_url() const { return manifest_url_; }
+ int64 response_id() const { return response_id_; }
+ const net::HttpResponseInfo* http_response_info() const {
+ return http_response_info_.get();
+ }
+ int64 response_data_size() const { return response_data_size_; }
+
+ private:
+ friend class base::RefCounted<AppCacheResponseInfo>;
+ virtual ~AppCacheResponseInfo();
+
+ const GURL manifest_url_;
+ const int64 response_id_;
+ const scoped_ptr<net::HttpResponseInfo> http_response_info_;
+ const int64 response_data_size_;
+ AppCacheStorage* storage_;
+};
+
+// A refcounted wrapper for HttpResponseInfo so we can apply the
+// refcounting semantics used with IOBuffer with these structures too.
+struct CONTENT_EXPORT HttpResponseInfoIOBuffer
+ : public base::RefCountedThreadSafe<HttpResponseInfoIOBuffer> {
+ scoped_ptr<net::HttpResponseInfo> http_info;
+ int response_data_size;
+
+ HttpResponseInfoIOBuffer();
+ explicit HttpResponseInfoIOBuffer(net::HttpResponseInfo* info);
+
+ protected:
+ friend class base::RefCountedThreadSafe<HttpResponseInfoIOBuffer>;
+ virtual ~HttpResponseInfoIOBuffer();
+};
+
+// Low level storage API used by the response reader and writer.
+class CONTENT_EXPORT AppCacheDiskCacheInterface {
+ public:
+ class Entry {
+ public:
+ virtual int Read(int index, int64 offset, net::IOBuffer* buf, int buf_len,
+ const net::CompletionCallback& callback) = 0;
+ virtual int Write(int index, int64 offset, net::IOBuffer* buf, int buf_len,
+ const net::CompletionCallback& callback) = 0;
+ virtual int64 GetSize(int index) = 0;
+ virtual void Close() = 0;
+ protected:
+ virtual ~Entry() {}
+ };
+
+ virtual int CreateEntry(int64 key, Entry** entry,
+ const net::CompletionCallback& callback) = 0;
+ virtual int OpenEntry(int64 key, Entry** entry,
+ const net::CompletionCallback& callback) = 0;
+ virtual int DoomEntry(int64 key, const net::CompletionCallback& callback) = 0;
+
+ protected:
+ friend class base::RefCounted<AppCacheDiskCacheInterface>;
+ virtual ~AppCacheDiskCacheInterface() {}
+};
+
+// Common base class for response reader and writer.
+class CONTENT_EXPORT AppCacheResponseIO {
+ public:
+ virtual ~AppCacheResponseIO();
+ int64 response_id() const { return response_id_; }
+
+ protected:
+ AppCacheResponseIO(int64 response_id,
+ int64 group_id,
+ AppCacheDiskCacheInterface* disk_cache);
+
+ virtual void OnIOComplete(int result) = 0;
+
+ bool IsIOPending() { return !callback_.is_null(); }
+ void ScheduleIOCompletionCallback(int result);
+ void InvokeUserCompletionCallback(int result);
+ void ReadRaw(int index, int offset, net::IOBuffer* buf, int buf_len);
+ void WriteRaw(int index, int offset, net::IOBuffer* buf, int buf_len);
+
+ const int64 response_id_;
+ const int64 group_id_;
+ AppCacheDiskCacheInterface* disk_cache_;
+ AppCacheDiskCacheInterface::Entry* entry_;
+ scoped_refptr<HttpResponseInfoIOBuffer> info_buffer_;
+ scoped_refptr<net::IOBuffer> buffer_;
+ int buffer_len_;
+ net::CompletionCallback callback_;
+ base::WeakPtrFactory<AppCacheResponseIO> weak_factory_;
+
+ private:
+ void OnRawIOComplete(int result);
+};
+
+// Reads existing response data from storage. If the object is deleted
+// and there is a read in progress, the implementation will return
+// immediately but will take care of any side effect of cancelling the
+// operation. In other words, instances are safe to delete at will.
+class CONTENT_EXPORT AppCacheResponseReader
+ : public AppCacheResponseIO {
+ public:
+ virtual ~AppCacheResponseReader();
+
+ // Reads http info from storage. Always returns the result of the read
+ // asynchronously through the 'callback'. Returns the number of bytes read
+ // or a net:: error code. Guaranteed to not perform partial reads of
+ // the info data. The reader acquires a reference to the 'info_buf' until
+ // completion at which time the callback is invoked with either a negative
+ // error code or the number of bytes read. The 'info_buf' argument should
+ // contain a NULL http_info when ReadInfo is called. The 'callback' is a
+ // required parameter.
+ // Should only be called where there is no Read operation in progress.
+ // (virtual for testing)
+ virtual void ReadInfo(HttpResponseInfoIOBuffer* info_buf,
+ const net::CompletionCallback& callback);
+
+ // Reads data from storage. Always returns the result of the read
+ // asynchronously through the 'callback'. Returns the number of bytes read
+ // or a net:: error code. EOF is indicated with a return value of zero.
+ // The reader acquires a reference to the provided 'buf' until completion
+ // at which time the callback is invoked with either a negative error code
+ // or the number of bytes read. The 'callback' is a required parameter.
+ // Should only be called where there is no Read operation in progress.
+ // (virtual for testing)
+ virtual void ReadData(net::IOBuffer* buf, int buf_len,
+ const net::CompletionCallback& callback);
+
+ // Returns true if there is a read operation, for data or info, pending.
+ bool IsReadPending() { return IsIOPending(); }
+
+ // Used to support range requests. If not called, the reader will
+ // read the entire response body. If called, this must be called prior
+ // to the first call to the ReadData method.
+ void SetReadRange(int offset, int length);
+
+ protected:
+ friend class AppCacheStorageImpl;
+ friend class content::MockAppCacheStorage;
+
+ // Should only be constructed by the storage class and derivatives.
+ AppCacheResponseReader(int64 response_id,
+ int64 group_id,
+ AppCacheDiskCacheInterface* disk_cache);
+
+ virtual void OnIOComplete(int result) OVERRIDE;
+ void ContinueReadInfo();
+ void ContinueReadData();
+ void OpenEntryIfNeededAndContinue();
+ void OnOpenEntryComplete(AppCacheDiskCacheInterface::Entry** entry, int rv);
+
+ int range_offset_;
+ int range_length_;
+ int read_position_;
+ net::CompletionCallback open_callback_;
+ base::WeakPtrFactory<AppCacheResponseReader> weak_factory_;
+};
+
+// Writes new response data to storage. If the object is deleted
+// and there is a write in progress, the implementation will return
+// immediately but will take care of any side effect of cancelling the
+// operation. In other words, instances are safe to delete at will.
+class CONTENT_EXPORT AppCacheResponseWriter
+ : public AppCacheResponseIO {
+ public:
+ virtual ~AppCacheResponseWriter();
+
+ // Writes the http info to storage. Always returns the result of the write
+ // asynchronously through the 'callback'. Returns the number of bytes written
+ // or a net:: error code. The writer acquires a reference to the 'info_buf'
+ // until completion at which time the callback is invoked with either a
+ // negative error code or the number of bytes written. The 'callback' is a
+ // required parameter. The contents of 'info_buf' are not modified.
+ // Should only be called where there is no Write operation in progress.
+ void WriteInfo(HttpResponseInfoIOBuffer* info_buf,
+ const net::CompletionCallback& callback);
+
+ // Writes data to storage. Always returns the result of the write
+ // asynchronously through the 'callback'. Returns the number of bytes written
+ // or a net:: error code. Guaranteed to not perform partial writes.
+ // The writer acquires a reference to the provided 'buf' until completion at
+ // which time the callback is invoked with either a negative error code or
+ // the number of bytes written. The 'callback' is a required parameter.
+ // The contents of 'buf' are not modified.
+ // Should only be called where there is no Write operation in progress.
+ void WriteData(net::IOBuffer* buf, int buf_len,
+ const net::CompletionCallback& callback);
+
+ // Returns true if there is a write pending.
+ bool IsWritePending() { return IsIOPending(); }
+
+ // Returns the amount written, info and data.
+ int64 amount_written() { return info_size_ + write_position_; }
+
+ protected:
+ // Should only be constructed by the storage class and derivatives.
+ AppCacheResponseWriter(int64 response_id,
+ int64 group_id,
+ AppCacheDiskCacheInterface* disk_cache);
+
+ private:
+ friend class AppCacheStorageImpl;
+ friend class content::MockAppCacheStorage;
+
+ enum CreationPhase {
+ NO_ATTEMPT,
+ INITIAL_ATTEMPT,
+ DOOM_EXISTING,
+ SECOND_ATTEMPT
+ };
+
+ virtual void OnIOComplete(int result) OVERRIDE;
+ void ContinueWriteInfo();
+ void ContinueWriteData();
+ void CreateEntryIfNeededAndContinue();
+ void OnCreateEntryComplete(AppCacheDiskCacheInterface::Entry** entry, int rv);
+
+ int info_size_;
+ int write_position_;
+ int write_amount_;
+ CreationPhase creation_phase_;
+ net::CompletionCallback create_callback_;
+ base::WeakPtrFactory<AppCacheResponseWriter> weak_factory_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_APPCACHE_APPCACHE_RESPONSE_H_
diff --git a/content/browser/appcache/appcache_response_unittest.cc b/content/browser/appcache/appcache_response_unittest.cc
index 935fd7c..49a3be8 100644
--- a/content/browser/appcache/appcache_response_unittest.cc
+++ b/content/browser/appcache/appcache_response_unittest.cc
@@ -13,18 +13,13 @@
#include "base/pickle.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
+#include "content/browser/appcache/appcache_response.h"
#include "content/browser/appcache/mock_appcache_service.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/http/http_response_headers.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/appcache/appcache_response.h"
-using appcache::AppCacheStorage;
-using appcache::AppCacheResponseInfo;
-using appcache::AppCacheResponseReader;
-using appcache::AppCacheResponseWriter;
-using appcache::HttpResponseInfoIOBuffer;
using net::IOBuffer;
using net::WrappedIOBuffer;
diff --git a/content/browser/appcache/appcache_service_impl.cc b/content/browser/appcache/appcache_service_impl.cc
new file mode 100644
index 0000000..6c3f0fd
--- /dev/null
+++ b/content/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 "content/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 "content/browser/appcache/appcache.h"
+#include "content/browser/appcache/appcache_backend_impl.h"
+#include "content/browser/appcache/appcache_entry.h"
+#include "content/browser/appcache/appcache_executable_handler.h"
+#include "content/browser/appcache/appcache_histograms.h"
+#include "content/browser/appcache/appcache_policy.h"
+#include "content/browser/appcache/appcache_quota_client.h"
+#include "content/browser/appcache/appcache_response.h"
+#include "content/browser/appcache/appcache_service_impl.h"
+#include "content/browser/appcache/appcache_storage_impl.h"
+#include "net/base/completion_callback.h"
+#include "net/base/io_buffer.h"
+#include "webkit/browser/quota/special_storage_policy.h"
+
+namespace content {
+
+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(
+ AppCacheGroup* group, const GURL& manifest_url) OVERRIDE;
+ virtual void OnGroupMadeObsolete(AppCacheGroup* group,
+ bool success,
+ int response_code) OVERRIDE;
+
+ GURL manifest_url_;
+ DISALLOW_COPY_AND_ASSIGN(DeleteHelper);
+};
+
+void AppCacheServiceImpl::DeleteHelper::OnGroupLoaded(
+ 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(
+ 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(
+ AppCacheGroup* group, const GURL& manifest_url) OVERRIDE;
+ virtual void OnGroupMadeObsolete(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(
+ 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(
+ 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 content
diff --git a/content/browser/appcache/appcache_service_impl.h b/content/browser/appcache/appcache_service_impl.h
new file mode 100644
index 0000000..ef4f060
--- /dev/null
+++ b/content/browser/appcache/appcache_service_impl.h
@@ -0,0 +1,227 @@
+// 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.
+
+#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_SERVICE_IMPL_H_
+#define CONTENT_BROWSER_APPCACHE_APPCACHE_SERVICE_IMPL_H_
+
+#include <map>
+#include <set>
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "content/common/appcache_interfaces.h"
+#include "content/common/content_export.h"
+#include "content/public/browser/appcache_service.h"
+#include "net/base/completion_callback.h"
+#include "net/base/net_errors.h"
+#include "webkit/browser/quota/quota_manager_proxy.h"
+
+namespace net {
+class URLRequestContext;
+} // namespace net
+
+namespace base {
+class FilePath;
+class MessageLoopProxy;
+}
+
+namespace content {
+FORWARD_DECLARE_TEST(AppCacheServiceImplTest, ScheduleReinitialize);
+class AppCacheServiceImplTest;
+class AppCacheStorageImplTest;
+}
+
+namespace quota {
+class SpecialStoragePolicy;
+}
+
+namespace content {
+
+class AppCacheBackendImpl;
+class AppCacheExecutableHandlerFactory;
+class AppCacheQuotaClient;
+class AppCachePolicy;
+class AppCacheStorage;
+
+// Refcounted container to manage the lifetime of the old storage instance
+// during Reinitialization.
+class CONTENT_EXPORT AppCacheStorageReference
+ : public base::RefCounted<AppCacheStorageReference> {
+public:
+ AppCacheStorage* storage() const { return storage_.get(); }
+private:
+ friend class AppCacheServiceImpl;
+ friend class base::RefCounted<AppCacheStorageReference>;
+ AppCacheStorageReference(scoped_ptr<AppCacheStorage> storage);
+ ~AppCacheStorageReference();
+
+ scoped_ptr<AppCacheStorage> storage_;
+};
+
+// Class that manages the application cache service. Sends notifications
+// to many frontends. One instance per user-profile. Each instance has
+// exclusive access to its cache_directory on disk.
+class CONTENT_EXPORT AppCacheServiceImpl
+ : public AppCacheService {
+ public:
+
+ class CONTENT_EXPORT Observer {
+ public:
+ // An observer method to inform consumers of reinitialzation. Managing
+ // the lifetime of the old storage instance is a delicate process.
+ // Consumers can keep the old disabled instance alive by hanging on to the
+ // ref provided.
+ virtual void OnServiceReinitialized(
+ AppCacheStorageReference* old_storage_ref) = 0;
+ virtual ~Observer() {}
+ };
+
+ // If not using quota management, the proxy may be NULL.
+ explicit AppCacheServiceImpl(quota::QuotaManagerProxy* quota_manager_proxy);
+ virtual ~AppCacheServiceImpl();
+
+ void Initialize(const base::FilePath& cache_directory,
+ base::MessageLoopProxy* db_thread,
+ base::MessageLoopProxy* cache_thread);
+
+ void AddObserver(Observer* observer) {
+ observers_.AddObserver(observer);
+ }
+
+ void RemoveObserver(Observer* observer) {
+ observers_.RemoveObserver(observer);
+ }
+
+ // For use in catastrophic failure modes to reboot the appcache system
+ // without relaunching the browser.
+ void ScheduleReinitialize();
+
+ // AppCacheService implementation:
+ virtual void CanHandleMainResourceOffline(
+ const GURL& url,
+ const GURL& first_party,
+ const net::CompletionCallback& callback) OVERRIDE;
+ virtual void GetAllAppCacheInfo(
+ AppCacheInfoCollection* collection,
+ const net::CompletionCallback& callback) OVERRIDE;
+ virtual void DeleteAppCacheGroup(
+ const GURL& manifest_url,
+ const net::CompletionCallback& callback) OVERRIDE;
+
+ // Deletes all appcaches for the origin, 'callback' is invoked upon
+ // completion. This method always completes asynchronously.
+ // (virtual for unit testing)
+ virtual void DeleteAppCachesForOrigin(
+ const GURL& origin, const net::CompletionCallback& callback);
+
+ // Checks the integrity of 'response_id' by reading the headers and data.
+ // If it cannot be read, the cache group for 'manifest_url' is deleted.
+ void CheckAppCacheResponse(const GURL& manifest_url, int64 cache_id,
+ int64 response_id);
+
+ // Context for use during cache updates, should only be accessed
+ // on the IO thread. We do NOT add a reference to the request context,
+ // it is the callers responsibility to ensure that the pointer
+ // remains valid while set.
+ net::URLRequestContext* request_context() const { return request_context_; }
+ void set_request_context(net::URLRequestContext* context) {
+ request_context_ = context;
+ }
+
+ // The appcache policy, may be null, in which case access is always allowed.
+ // The service does NOT assume ownership of the policy, it is the callers
+ // responsibility to ensure that the pointer remains valid while set.
+ AppCachePolicy* appcache_policy() const { return appcache_policy_; }
+ void set_appcache_policy(AppCachePolicy* policy) {
+ appcache_policy_ = policy;
+ }
+
+ // The factory may be null, in which case invocations of exe handlers
+ // will result in an error response.
+ // The service does NOT assume ownership of the factory, it is the callers
+ // responsibility to ensure that the pointer remains valid while set.
+ AppCacheExecutableHandlerFactory* handler_factory() const {
+ return handler_factory_;
+ }
+ void set_handler_factory(
+ AppCacheExecutableHandlerFactory* factory) {
+ handler_factory_ = factory;
+ }
+
+ quota::SpecialStoragePolicy* special_storage_policy() const {
+ return special_storage_policy_.get();
+ }
+ void set_special_storage_policy(quota::SpecialStoragePolicy* policy);
+
+ quota::QuotaManagerProxy* quota_manager_proxy() const {
+ return quota_manager_proxy_.get();
+ }
+
+ AppCacheQuotaClient* quota_client() const {
+ return quota_client_;
+ }
+
+ // Each child process in chrome uses a distinct backend instance.
+ // See chrome/browser/AppCacheDispatcherHost.
+ void RegisterBackend(AppCacheBackendImpl* backend_impl);
+ void UnregisterBackend(AppCacheBackendImpl* backend_impl);
+ AppCacheBackendImpl* GetBackend(int id) const {
+ BackendMap::const_iterator it = backends_.find(id);
+ return (it != backends_.end()) ? it->second : NULL;
+ }
+
+ AppCacheStorage* storage() const { return storage_.get(); }
+
+ // Disables the exit-time deletion of session-only data.
+ void set_force_keep_session_state() { force_keep_session_state_ = true; }
+ bool force_keep_session_state() const { return force_keep_session_state_; }
+
+ protected:
+ friend class content::AppCacheServiceImplTest;
+ friend class content::AppCacheStorageImplTest;
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheServiceImplTest,
+ ScheduleReinitialize);
+
+ class AsyncHelper;
+ class CanHandleOfflineHelper;
+ class DeleteHelper;
+ class DeleteOriginHelper;
+ class GetInfoHelper;
+ class CheckResponseHelper;
+
+ typedef std::set<AsyncHelper*> PendingAsyncHelpers;
+ typedef std::map<int, AppCacheBackendImpl*> BackendMap;
+
+ void Reinitialize();
+
+ base::FilePath cache_directory_;
+ scoped_refptr<base::MessageLoopProxy> db_thread_;
+ scoped_refptr<base::MessageLoopProxy> cache_thread_;
+ AppCachePolicy* appcache_policy_;
+ AppCacheQuotaClient* quota_client_;
+ AppCacheExecutableHandlerFactory* handler_factory_;
+ scoped_ptr<AppCacheStorage> storage_;
+ scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy_;
+ scoped_refptr<quota::QuotaManagerProxy> quota_manager_proxy_;
+ PendingAsyncHelpers pending_helpers_;
+ BackendMap backends_; // One 'backend' per child process.
+ // Context for use during cache updates.
+ net::URLRequestContext* request_context_;
+ // If true, nothing (not even session-only data) should be deleted on exit.
+ bool force_keep_session_state_;
+ base::Time last_reinit_time_;
+ base::TimeDelta next_reinit_delay_;
+ base::OneShotTimer<AppCacheServiceImpl> reinit_timer_;
+ ObserverList<Observer> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppCacheServiceImpl);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_APPCACHE_APPCACHE_SERVICE_IMPL_H_
diff --git a/content/browser/appcache/appcache_service_unittest.cc b/content/browser/appcache/appcache_service_unittest.cc
index 80016a9..d22fb041 100644
--- a/content/browser/appcache/appcache_service_unittest.cc
+++ b/content/browser/appcache/appcache_service_unittest.cc
@@ -8,23 +8,13 @@
#include "base/bind_helpers.h"
#include "base/pickle.h"
#include "base/run_loop.h"
+#include "content/browser/appcache/appcache_response.h"
+#include "content/browser/appcache/appcache_service_impl.h"
#include "content/browser/appcache/mock_appcache_storage.h"
#include "net/base/completion_callback.h"
#include "net/base/io_buffer.h"
#include "net/http/http_response_headers.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/appcache/appcache_response.h"
-#include "webkit/browser/appcache/appcache_service_impl.h"
-
-using appcache::AppCache;
-using appcache::AppCacheEntry;
-using appcache::AppCacheGroup;
-using appcache::AppCacheInfo;
-using appcache::AppCacheInfoCollection;
-using appcache::AppCacheInfoVector;
-using appcache::AppCacheResponseReader;
-using appcache::AppCacheServiceImpl;
-using appcache::HttpResponseInfoIOBuffer;
namespace content {
namespace {
@@ -199,7 +189,7 @@ TEST_F(AppCacheServiceImplTest, DeleteAppCachesForOrigin) {
delete_completion_count_ = 0;
// Should succeed given an empty info collection.
- mock_storage()->SimulateGetAllInfo(new AppCacheInfoCollection);
+ mock_storage()->SimulateGetAllInfo(new content::AppCacheInfoCollection);
service_->DeleteAppCachesForOrigin(kOrigin, deletion_callback_);
EXPECT_EQ(0, delete_completion_count_);
base::RunLoop().RunUntilIdle();
diff --git a/content/browser/appcache/appcache_storage.cc b/content/browser/appcache/appcache_storage.cc
new file mode 100644
index 0000000..d78339d
--- /dev/null
+++ b/content/browser/appcache/appcache_storage.cc
@@ -0,0 +1,136 @@
+// Copyright (c) 2009 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 "content/browser/appcache/appcache_storage.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/stl_util.h"
+#include "content/browser/appcache/appcache_response.h"
+#include "content/browser/appcache/appcache_service_impl.h"
+#include "webkit/browser/quota/quota_client.h"
+#include "webkit/browser/quota/quota_manager_proxy.h"
+
+namespace content {
+
+// static
+const int64 AppCacheStorage::kUnitializedId = -1;
+
+AppCacheStorage::AppCacheStorage(AppCacheServiceImpl* service)
+ : last_cache_id_(kUnitializedId), last_group_id_(kUnitializedId),
+ last_response_id_(kUnitializedId), service_(service) {
+}
+
+AppCacheStorage::~AppCacheStorage() {
+ STLDeleteValues(&pending_info_loads_);
+ DCHECK(delegate_references_.empty());
+}
+
+AppCacheStorage::DelegateReference::DelegateReference(
+ Delegate* delegate, AppCacheStorage* storage)
+ : delegate(delegate), storage(storage) {
+ storage->delegate_references_.insert(
+ DelegateReferenceMap::value_type(delegate, this));
+}
+
+AppCacheStorage::DelegateReference::~DelegateReference() {
+ if (delegate)
+ storage->delegate_references_.erase(delegate);
+}
+
+AppCacheStorage::ResponseInfoLoadTask::ResponseInfoLoadTask(
+ const GURL& manifest_url,
+ int64 group_id,
+ int64 response_id,
+ AppCacheStorage* storage)
+ : storage_(storage),
+ manifest_url_(manifest_url),
+ group_id_(group_id),
+ response_id_(response_id),
+ info_buffer_(new HttpResponseInfoIOBuffer) {
+ storage_->pending_info_loads_.insert(
+ PendingResponseInfoLoads::value_type(response_id, this));
+}
+
+AppCacheStorage::ResponseInfoLoadTask::~ResponseInfoLoadTask() {
+}
+
+void AppCacheStorage::ResponseInfoLoadTask::StartIfNeeded() {
+ if (reader_)
+ return;
+ reader_.reset(
+ storage_->CreateResponseReader(manifest_url_, group_id_, response_id_));
+ reader_->ReadInfo(info_buffer_.get(),
+ base::Bind(&ResponseInfoLoadTask::OnReadComplete,
+ base::Unretained(this)));
+}
+
+void AppCacheStorage::ResponseInfoLoadTask::OnReadComplete(int result) {
+ storage_->pending_info_loads_.erase(response_id_);
+ scoped_refptr<AppCacheResponseInfo> info;
+ if (result >= 0) {
+ info = new AppCacheResponseInfo(storage_, manifest_url_,
+ response_id_,
+ info_buffer_->http_info.release(),
+ info_buffer_->response_data_size);
+ }
+ FOR_EACH_DELEGATE(delegates_, OnResponseInfoLoaded(info.get(), response_id_));
+ delete this;
+}
+
+void AppCacheStorage::LoadResponseInfo(
+ const GURL& manifest_url, int64 group_id, int64 id, Delegate* delegate) {
+ AppCacheResponseInfo* info = working_set_.GetResponseInfo(id);
+ if (info) {
+ delegate->OnResponseInfoLoaded(info, id);
+ return;
+ }
+ ResponseInfoLoadTask* info_load =
+ GetOrCreateResponseInfoLoadTask(manifest_url, group_id, id);
+ DCHECK(manifest_url == info_load->manifest_url());
+ DCHECK(group_id == info_load->group_id());
+ DCHECK(id == info_load->response_id());
+ info_load->AddDelegate(GetOrCreateDelegateReference(delegate));
+ info_load->StartIfNeeded();
+}
+
+void AppCacheStorage::UpdateUsageMapAndNotify(
+ const GURL& origin, int64 new_usage) {
+ DCHECK_GE(new_usage, 0);
+ int64 old_usage = usage_map_[origin];
+ if (new_usage > 0)
+ usage_map_[origin] = new_usage;
+ else
+ usage_map_.erase(origin);
+ if (new_usage != old_usage && service()->quota_manager_proxy()) {
+ service()->quota_manager_proxy()->NotifyStorageModified(
+ quota::QuotaClient::kAppcache,
+ origin, quota::kStorageTypeTemporary,
+ new_usage - old_usage);
+ }
+}
+
+void AppCacheStorage::ClearUsageMapAndNotify() {
+ if (service()->quota_manager_proxy()) {
+ for (UsageMap::const_iterator iter = usage_map_.begin();
+ iter != usage_map_.end(); ++iter) {
+ service()->quota_manager_proxy()->NotifyStorageModified(
+ quota::QuotaClient::kAppcache,
+ iter->first, quota::kStorageTypeTemporary,
+ -(iter->second));
+ }
+ }
+ usage_map_.clear();
+}
+
+void AppCacheStorage::NotifyStorageAccessed(const GURL& origin) {
+ if (service()->quota_manager_proxy() &&
+ usage_map_.find(origin) != usage_map_.end())
+ service()->quota_manager_proxy()->NotifyStorageAccessed(
+ quota::QuotaClient::kAppcache,
+ origin, quota::kStorageTypeTemporary);
+}
+
+} // namespace content
+
diff --git a/content/browser/appcache/appcache_storage.h b/content/browser/appcache/appcache_storage.h
new file mode 100644
index 0000000..871110c
--- /dev/null
+++ b/content/browser/appcache/appcache_storage.h
@@ -0,0 +1,330 @@
+// Copyright (c) 2012 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.
+
+#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_STORAGE_H_
+#define CONTENT_BROWSER_APPCACHE_APPCACHE_STORAGE_H_
+
+#include <map>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/appcache/appcache_working_set.h"
+#include "content/common/content_export.h"
+#include "net/base/completion_callback.h"
+
+class GURL;
+
+namespace content {
+FORWARD_DECLARE_TEST(AppCacheStorageTest, DelegateReferences);
+FORWARD_DECLARE_TEST(AppCacheStorageTest, UsageMap);
+class AppCacheQuotaClientTest;
+class AppCacheResponseTest;
+class AppCacheStorageTest;
+}
+
+namespace content {
+
+class AppCache;
+class AppCacheEntry;
+class AppCacheGroup;
+class AppCacheResponseReader;
+class AppCacheResponseWriter;
+class AppCacheServiceImpl;
+struct AppCacheInfoCollection;
+struct HttpResponseInfoIOBuffer;
+
+class CONTENT_EXPORT AppCacheStorage {
+ public:
+ typedef std::map<GURL, int64> UsageMap;
+
+ class CONTENT_EXPORT Delegate {
+ public:
+ // If retrieval fails, 'collection' will be NULL.
+ virtual void OnAllInfo(AppCacheInfoCollection* collection) {}
+
+ // If a load fails the 'cache' will be NULL.
+ virtual void OnCacheLoaded(AppCache* cache, int64 cache_id) {}
+
+ // If a load fails the 'group' will be NULL.
+ virtual void OnGroupLoaded(
+ AppCacheGroup* group, const GURL& manifest_url) {}
+
+ // If successfully stored 'success' will be true.
+ virtual void OnGroupAndNewestCacheStored(
+ AppCacheGroup* group, AppCache* newest_cache, bool success,
+ bool would_exceed_quota) {}
+
+ // If the operation fails, success will be false.
+ virtual void OnGroupMadeObsolete(AppCacheGroup* group,
+ bool success,
+ int response_code) {}
+
+ // If a load fails the 'response_info' will be NULL.
+ virtual void OnResponseInfoLoaded(
+ AppCacheResponseInfo* response_info, int64 response_id) {}
+
+ // If no response is found, entry.response_id() and
+ // fallback_entry.response_id() will be kAppCacheNoResponseId.
+ // If the response is the entry for an intercept or fallback
+ // namespace, the url of the namespece entry is returned.
+ // If a response is found, the cache id and manifest url of the
+ // containing cache and group are also returned.
+ virtual void OnMainResponseFound(
+ const GURL& url, const AppCacheEntry& entry,
+ const GURL& namespace_entry_url, const AppCacheEntry& fallback_entry,
+ int64 cache_id, int64 group_id, const GURL& mainfest_url) {}
+
+ protected:
+ virtual ~Delegate() {}
+ };
+
+ explicit AppCacheStorage(AppCacheServiceImpl* service);
+ virtual ~AppCacheStorage();
+
+ // Schedules a task to retrieve basic info about all groups and caches
+ // stored in the system. Upon completion the delegate will be called
+ // with the results.
+ virtual void GetAllInfo(Delegate* delegate) = 0;
+
+ // Schedules a cache to be loaded from storage. Upon load completion
+ // the delegate will be called back. If the cache already resides in
+ // memory, the delegate will be called back immediately without returning
+ // to the message loop. If the load fails, the delegate will be called
+ // back with a NULL cache pointer.
+ virtual void LoadCache(int64 id, Delegate* delegate) = 0;
+
+ // Schedules a group and its newest cache, if any, to be loaded from storage.
+ // Upon load completion the delegate will be called back. If the group
+ // and newest cache already reside in memory, the delegate will be called
+ // back immediately without returning to the message loop. If the load fails,
+ // the delegate will be called back with a NULL group pointer.
+ virtual void LoadOrCreateGroup(
+ const GURL& manifest_url, Delegate* delegate) = 0;
+
+ // Schedules response info to be loaded from storage.
+ // Upon load completion the delegate will be called back. If the data
+ // already resides in memory, the delegate will be called back
+ // immediately without returning to the message loop. If the load fails,
+ // the delegate will be called back with a NULL pointer.
+ virtual void LoadResponseInfo(
+ const GURL& manifest_url, int64 group_id, int64 response_id,
+ Delegate* delegate);
+
+ // Schedules a group and its newest complete cache to be initially stored or
+ // incrementally updated with new changes. Upon completion the delegate
+ // will be called back. A group without a newest cache cannot be stored.
+ // It's a programming error to call this method without a newest cache. A
+ // side effect of storing a new newest cache is the removal of the group's
+ // old caches and responses from persistent storage (although they may still
+ // linger in the in-memory working set until no longer needed). The new
+ // cache will be added as the group's newest complete cache only if storage
+ // succeeds.
+ virtual void StoreGroupAndNewestCache(
+ AppCacheGroup* group, AppCache* newest_cache, Delegate* delegate) = 0;
+
+ // Schedules a query to identify a response for a main request. Upon
+ // completion the delegate will be called back.
+ virtual void FindResponseForMainRequest(
+ const GURL& url,
+ const GURL& preferred_manifest_url,
+ Delegate* delegate) = 0;
+
+ // Performs an immediate lookup of the in-memory cache to
+ // identify a response for a sub resource request.
+ virtual void FindResponseForSubRequest(
+ AppCache* cache, const GURL& url,
+ AppCacheEntry* found_entry, AppCacheEntry* found_fallback_entry,
+ bool* found_network_namespace) = 0;
+
+ // Immediately updates in-memory storage, if the cache is in memory,
+ // and schedules a task to update persistent storage. If the cache is
+ // already scheduled to be loaded, upon loading completion the entry
+ // will be marked. There is no delegate completion callback.
+ virtual void MarkEntryAsForeign(const GURL& entry_url, int64 cache_id) = 0;
+
+ // Schedules a task to update persistent storage and doom the group and all
+ // related caches and responses for deletion. Upon completion the in-memory
+ // instance is marked as obsolete and the delegate callback is called.
+ virtual void MakeGroupObsolete(AppCacheGroup* group,
+ Delegate* delegate,
+ int response_code) = 0;
+
+ // Cancels all pending callbacks for the delegate. The delegate callbacks
+ // will not be invoked after, however any scheduled operations will still
+ // take place. The callbacks for subsequently scheduled operations are
+ // unaffected.
+ void CancelDelegateCallbacks(Delegate* delegate) {
+ DelegateReference* delegate_reference = GetDelegateReference(delegate);
+ if (delegate_reference)
+ delegate_reference->CancelReference();
+ }
+
+ // Creates a reader to read a response from storage.
+ virtual AppCacheResponseReader* CreateResponseReader(
+ const GURL& manifest_url, int64 group_id, int64 response_id) = 0;
+
+ // Creates a writer to write a new response to storage. This call
+ // establishes a new response id.
+ virtual AppCacheResponseWriter* CreateResponseWriter(
+ const GURL& manifest_url, int64 group_id) = 0;
+
+ // Schedules the lazy deletion of responses and saves the ids
+ // persistently such that the responses will be deleted upon restart
+ // if they aren't deleted prior to shutdown.
+ virtual void DoomResponses(
+ const GURL& manifest_url, const std::vector<int64>& response_ids) = 0;
+
+ // Schedules the lazy deletion of responses without persistently saving
+ // the response ids.
+ virtual void DeleteResponses(
+ const GURL& manifest_url, const std::vector<int64>& response_ids) = 0;
+
+ // Generates unique storage ids for different object types.
+ int64 NewCacheId() {
+ return ++last_cache_id_;
+ }
+ int64 NewGroupId() {
+ return ++last_group_id_;
+ }
+
+ // The working set of object instances currently in memory.
+ AppCacheWorkingSet* working_set() { return &working_set_; }
+
+ // A map of origins to usage.
+ const UsageMap* usage_map() { return &usage_map_; }
+
+ // Simple ptr back to the service object that owns us.
+ AppCacheServiceImpl* service() { return service_; }
+
+ protected:
+ friend class content::AppCacheQuotaClientTest;
+ friend class content::AppCacheResponseTest;
+ friend class content::AppCacheStorageTest;
+
+ // Helper to call a collection of delegates.
+ #define FOR_EACH_DELEGATE(delegates, func_and_args) \
+ do { \
+ for (DelegateReferenceVector::iterator it = delegates.begin(); \
+ it != delegates.end(); ++it) { \
+ if (it->get()->delegate) \
+ it->get()->delegate->func_and_args; \
+ } \
+ } while (0)
+
+ // Helper used to manage multiple references to a 'delegate' and to
+ // allow all pending callbacks to that delegate to be easily cancelled.
+ struct DelegateReference : public base::RefCounted<DelegateReference> {
+ Delegate* delegate;
+ AppCacheStorage* storage;
+
+ DelegateReference(Delegate* delegate, AppCacheStorage* storage);
+
+ void CancelReference() {
+ storage->delegate_references_.erase(delegate);
+ storage = NULL;
+ delegate = NULL;
+ }
+
+ private:
+ friend class base::RefCounted<DelegateReference>;
+
+ virtual ~DelegateReference();
+ };
+ typedef std::map<Delegate*, DelegateReference*> DelegateReferenceMap;
+ typedef std::vector<scoped_refptr<DelegateReference> >
+ DelegateReferenceVector;
+
+ // Helper used to manage an async LoadResponseInfo calls on behalf of
+ // multiple callers.
+ class ResponseInfoLoadTask {
+ public:
+ ResponseInfoLoadTask(const GURL& manifest_url, int64 group_id,
+ int64 response_id, AppCacheStorage* storage);
+ ~ResponseInfoLoadTask();
+
+ int64 response_id() const { return response_id_; }
+ const GURL& manifest_url() const { return manifest_url_; }
+ int64 group_id() const { return group_id_; }
+
+ void AddDelegate(DelegateReference* delegate_reference) {
+ delegates_.push_back(delegate_reference);
+ }
+
+ void StartIfNeeded();
+
+ private:
+ void OnReadComplete(int result);
+
+ AppCacheStorage* storage_;
+ GURL manifest_url_;
+ int64 group_id_;
+ int64 response_id_;
+ scoped_ptr<AppCacheResponseReader> reader_;
+ DelegateReferenceVector delegates_;
+ scoped_refptr<HttpResponseInfoIOBuffer> info_buffer_;
+ };
+
+ typedef std::map<int64, ResponseInfoLoadTask*> PendingResponseInfoLoads;
+
+ DelegateReference* GetDelegateReference(Delegate* delegate) {
+ DelegateReferenceMap::iterator iter =
+ delegate_references_.find(delegate);
+ if (iter != delegate_references_.end())
+ return iter->second;
+ return NULL;
+ }
+
+ DelegateReference* GetOrCreateDelegateReference(Delegate* delegate) {
+ DelegateReference* reference = GetDelegateReference(delegate);
+ if (reference)
+ return reference;
+ return new DelegateReference(delegate, this);
+ }
+
+ ResponseInfoLoadTask* GetOrCreateResponseInfoLoadTask(
+ const GURL& manifest_url, int64 group_id, int64 response_id) {
+ PendingResponseInfoLoads::iterator iter =
+ pending_info_loads_.find(response_id);
+ if (iter != pending_info_loads_.end())
+ return iter->second;
+ return new ResponseInfoLoadTask(manifest_url, group_id, response_id, this);
+ }
+
+ // Should only be called when creating a new response writer.
+ int64 NewResponseId() {
+ return ++last_response_id_;
+ }
+
+ // Helpers to query and notify the QuotaManager.
+ void UpdateUsageMapAndNotify(const GURL& origin, int64 new_usage);
+ void ClearUsageMapAndNotify();
+ void NotifyStorageAccessed(const GURL& origin);
+
+ // The last storage id used for different object types.
+ int64 last_cache_id_;
+ int64 last_group_id_;
+ int64 last_response_id_;
+
+ UsageMap usage_map_; // maps origin to usage
+ AppCacheWorkingSet working_set_;
+ AppCacheServiceImpl* service_;
+ DelegateReferenceMap delegate_references_;
+ PendingResponseInfoLoads pending_info_loads_;
+
+ // The set of last ids must be retrieved from storage prior to being used.
+ static const int64 kUnitializedId;
+
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheStorageTest, DelegateReferences);
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheStorageTest, UsageMap);
+
+ DISALLOW_COPY_AND_ASSIGN(AppCacheStorage);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_APPCACHE_APPCACHE_STORAGE_H_
diff --git a/content/browser/appcache/appcache_storage_impl.cc b/content/browser/appcache/appcache_storage_impl.cc
new file mode 100644
index 0000000..dee1f9c
--- /dev/null
+++ b/content/browser/appcache/appcache_storage_impl.cc
@@ -0,0 +1,1860 @@
+// Copyright (c) 2012 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 "content/browser/appcache/appcache_storage_impl.h"
+
+#include <algorithm>
+#include <functional>
+#include <set>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "content/browser/appcache/appcache.h"
+#include "content/browser/appcache/appcache_database.h"
+#include "content/browser/appcache/appcache_entry.h"
+#include "content/browser/appcache/appcache_group.h"
+#include "content/browser/appcache/appcache_histograms.h"
+#include "content/browser/appcache/appcache_quota_client.h"
+#include "content/browser/appcache/appcache_response.h"
+#include "content/browser/appcache/appcache_service_impl.h"
+#include "net/base/cache_type.h"
+#include "net/base/net_errors.h"
+#include "sql/connection.h"
+#include "sql/transaction.h"
+#include "webkit/browser/quota/quota_client.h"
+#include "webkit/browser/quota/quota_manager.h"
+#include "webkit/browser/quota/quota_manager_proxy.h"
+#include "webkit/browser/quota/special_storage_policy.h"
+
+namespace content {
+
+// Hard coded default when not using quota management.
+static const int kDefaultQuota = 5 * 1024 * 1024;
+
+static const int kMaxDiskCacheSize = 250 * 1024 * 1024;
+static const int kMaxMemDiskCacheSize = 10 * 1024 * 1024;
+static const base::FilePath::CharType kDiskCacheDirectoryName[] =
+ FILE_PATH_LITERAL("Cache");
+
+namespace {
+
+// Helpers for clearing data from the AppCacheDatabase.
+bool DeleteGroupAndRelatedRecords(AppCacheDatabase* database,
+ int64 group_id,
+ std::vector<int64>* deletable_response_ids) {
+ AppCacheDatabase::CacheRecord cache_record;
+ bool success = false;
+ if (database->FindCacheForGroup(group_id, &cache_record)) {
+ database->FindResponseIdsForCacheAsVector(cache_record.cache_id,
+ deletable_response_ids);
+ success =
+ database->DeleteGroup(group_id) &&
+ database->DeleteCache(cache_record.cache_id) &&
+ database->DeleteEntriesForCache(cache_record.cache_id) &&
+ database->DeleteNamespacesForCache(cache_record.cache_id) &&
+ database->DeleteOnlineWhiteListForCache(cache_record.cache_id) &&
+ database->InsertDeletableResponseIds(*deletable_response_ids);
+ } else {
+ NOTREACHED() << "A existing group without a cache is unexpected";
+ success = database->DeleteGroup(group_id);
+ }
+ return success;
+}
+
+// Destroys |database|. If there is appcache data to be deleted
+// (|force_keep_session_state| is false), deletes session-only appcache data.
+void ClearSessionOnlyOrigins(
+ AppCacheDatabase* database,
+ scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy,
+ bool force_keep_session_state) {
+ scoped_ptr<AppCacheDatabase> database_to_delete(database);
+
+ // If saving session state, only delete the database.
+ if (force_keep_session_state)
+ return;
+
+ bool has_session_only_appcaches =
+ special_storage_policy.get() &&
+ special_storage_policy->HasSessionOnlyOrigins();
+
+ // Clearning only session-only databases, and there are none.
+ if (!has_session_only_appcaches)
+ return;
+
+ std::set<GURL> origins;
+ database->FindOriginsWithGroups(&origins);
+ if (origins.empty())
+ return; // nothing to delete
+
+ sql::Connection* connection = database->db_connection();
+ if (!connection) {
+ NOTREACHED() << "Missing database connection.";
+ return;
+ }
+
+ std::set<GURL>::const_iterator origin;
+ for (origin = origins.begin(); origin != origins.end(); ++origin) {
+ if (!special_storage_policy->IsStorageSessionOnly(*origin))
+ continue;
+ if (special_storage_policy.get() &&
+ special_storage_policy->IsStorageProtected(*origin))
+ continue;
+
+ std::vector<AppCacheDatabase::GroupRecord> groups;
+ database->FindGroupsForOrigin(*origin, &groups);
+ std::vector<AppCacheDatabase::GroupRecord>::const_iterator group;
+ for (group = groups.begin(); group != groups.end(); ++group) {
+ sql::Transaction transaction(connection);
+ if (!transaction.Begin()) {
+ NOTREACHED() << "Failed to start transaction";
+ return;
+ }
+ std::vector<int64> deletable_response_ids;
+ bool success = DeleteGroupAndRelatedRecords(database,
+ group->group_id,
+ &deletable_response_ids);
+ success = success && transaction.Commit();
+ DCHECK(success);
+ } // for each group
+ } // for each origin
+}
+
+} // namespace
+
+// DatabaseTask -----------------------------------------
+
+class AppCacheStorageImpl::DatabaseTask
+ : public base::RefCountedThreadSafe<DatabaseTask> {
+ public:
+ explicit DatabaseTask(AppCacheStorageImpl* storage)
+ : storage_(storage), database_(storage->database_),
+ io_thread_(base::MessageLoopProxy::current()) {
+ DCHECK(io_thread_.get());
+ }
+
+ void AddDelegate(DelegateReference* delegate_reference) {
+ delegates_.push_back(make_scoped_refptr(delegate_reference));
+ }
+
+ // Schedules a task to be Run() on the DB thread. Tasks
+ // are run in the order in which they are scheduled.
+ void Schedule();
+
+ // Called on the DB thread.
+ virtual void Run() = 0;
+
+ // Called on the IO thread after Run() has completed.
+ virtual void RunCompleted() {}
+
+ // Once scheduled a task cannot be cancelled, but the
+ // call to RunCompleted may be. This method should only be
+ // called on the IO thread. This is used by AppCacheStorageImpl
+ // to cancel the completion calls when AppCacheStorageImpl is
+ // destructed. This method may be overriden to release or delete
+ // additional data associated with the task that is not DB thread
+ // safe. If overriden, this base class method must be called from
+ // within the override.
+ virtual void CancelCompletion();
+
+ protected:
+ friend class base::RefCountedThreadSafe<DatabaseTask>;
+ virtual ~DatabaseTask() {}
+
+ AppCacheStorageImpl* storage_;
+ AppCacheDatabase* database_;
+ DelegateReferenceVector delegates_;
+
+ private:
+ void CallRun(base::TimeTicks schedule_time);
+ void CallRunCompleted(base::TimeTicks schedule_time);
+ void OnFatalError();
+
+ scoped_refptr<base::MessageLoopProxy> io_thread_;
+};
+
+void AppCacheStorageImpl::DatabaseTask::Schedule() {
+ DCHECK(storage_);
+ DCHECK(io_thread_->BelongsToCurrentThread());
+ if (!storage_->database_)
+ return;
+
+ if (storage_->db_thread_->PostTask(
+ FROM_HERE,
+ base::Bind(&DatabaseTask::CallRun, this, base::TimeTicks::Now()))) {
+ storage_->scheduled_database_tasks_.push_back(this);
+ } else {
+ NOTREACHED() << "Thread for database tasks is not running.";
+ }
+}
+
+void AppCacheStorageImpl::DatabaseTask::CancelCompletion() {
+ DCHECK(io_thread_->BelongsToCurrentThread());
+ delegates_.clear();
+ storage_ = NULL;
+}
+
+void AppCacheStorageImpl::DatabaseTask::CallRun(
+ base::TimeTicks schedule_time) {
+ AppCacheHistograms::AddTaskQueueTimeSample(
+ base::TimeTicks::Now() - schedule_time);
+ if (!database_->is_disabled()) {
+ base::TimeTicks run_time = base::TimeTicks::Now();
+ Run();
+ AppCacheHistograms::AddTaskRunTimeSample(
+ base::TimeTicks::Now() - run_time);
+
+ if (database_->was_corruption_detected()) {
+ AppCacheHistograms::CountCorruptionDetected();
+ database_->Disable();
+ }
+ if (database_->is_disabled()) {
+ io_thread_->PostTask(
+ FROM_HERE,
+ base::Bind(&DatabaseTask::OnFatalError, this));
+ }
+ }
+ io_thread_->PostTask(
+ FROM_HERE,
+ base::Bind(&DatabaseTask::CallRunCompleted, this,
+ base::TimeTicks::Now()));
+}
+
+void AppCacheStorageImpl::DatabaseTask::CallRunCompleted(
+ base::TimeTicks schedule_time) {
+ AppCacheHistograms::AddCompletionQueueTimeSample(
+ base::TimeTicks::Now() - schedule_time);
+ if (storage_) {
+ DCHECK(io_thread_->BelongsToCurrentThread());
+ DCHECK(storage_->scheduled_database_tasks_.front() == this);
+ storage_->scheduled_database_tasks_.pop_front();
+ base::TimeTicks run_time = base::TimeTicks::Now();
+ RunCompleted();
+ AppCacheHistograms::AddCompletionRunTimeSample(
+ base::TimeTicks::Now() - run_time);
+ delegates_.clear();
+ }
+}
+
+void AppCacheStorageImpl::DatabaseTask::OnFatalError() {
+ if (storage_) {
+ DCHECK(io_thread_->BelongsToCurrentThread());
+ storage_->Disable();
+ storage_->DeleteAndStartOver();
+ }
+}
+
+// InitTask -------
+
+class AppCacheStorageImpl::InitTask : public DatabaseTask {
+ public:
+ explicit InitTask(AppCacheStorageImpl* storage)
+ : DatabaseTask(storage), last_group_id_(0),
+ last_cache_id_(0), last_response_id_(0),
+ last_deletable_response_rowid_(0) {
+ if (!storage->is_incognito_) {
+ db_file_path_ =
+ storage->cache_directory_.Append(kAppCacheDatabaseName);
+ disk_cache_directory_ =
+ storage->cache_directory_.Append(kDiskCacheDirectoryName);
+ }
+ }
+
+ // DatabaseTask:
+ virtual void Run() OVERRIDE;
+ virtual void RunCompleted() OVERRIDE;
+
+ protected:
+ virtual ~InitTask() {}
+
+ private:
+ base::FilePath db_file_path_;
+ base::FilePath disk_cache_directory_;
+ int64 last_group_id_;
+ int64 last_cache_id_;
+ int64 last_response_id_;
+ int64 last_deletable_response_rowid_;
+ std::map<GURL, int64> usage_map_;
+};
+
+void AppCacheStorageImpl::InitTask::Run() {
+ // If there is no sql database, ensure there is no disk cache either.
+ if (!db_file_path_.empty() &&
+ !base::PathExists(db_file_path_) &&
+ base::DirectoryExists(disk_cache_directory_)) {
+ base::DeleteFile(disk_cache_directory_, true);
+ if (base::DirectoryExists(disk_cache_directory_)) {
+ database_->Disable(); // This triggers OnFatalError handling.
+ return;
+ }
+ }
+
+ database_->FindLastStorageIds(
+ &last_group_id_, &last_cache_id_, &last_response_id_,
+ &last_deletable_response_rowid_);
+ database_->GetAllOriginUsage(&usage_map_);
+}
+
+void AppCacheStorageImpl::InitTask::RunCompleted() {
+ storage_->last_group_id_ = last_group_id_;
+ storage_->last_cache_id_ = last_cache_id_;
+ storage_->last_response_id_ = last_response_id_;
+ storage_->last_deletable_response_rowid_ = last_deletable_response_rowid_;
+
+ if (!storage_->is_disabled()) {
+ storage_->usage_map_.swap(usage_map_);
+ const base::TimeDelta kDelay = base::TimeDelta::FromMinutes(5);
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&AppCacheStorageImpl::DelayedStartDeletingUnusedResponses,
+ storage_->weak_factory_.GetWeakPtr()),
+ kDelay);
+ }
+
+ if (storage_->service()->quota_client())
+ storage_->service()->quota_client()->NotifyAppCacheReady();
+}
+
+// DisableDatabaseTask -------
+
+class AppCacheStorageImpl::DisableDatabaseTask : public DatabaseTask {
+ public:
+ explicit DisableDatabaseTask(AppCacheStorageImpl* storage)
+ : DatabaseTask(storage) {}
+
+ // DatabaseTask:
+ virtual void Run() OVERRIDE { database_->Disable(); }
+
+ protected:
+ virtual ~DisableDatabaseTask() {}
+};
+
+// GetAllInfoTask -------
+
+class AppCacheStorageImpl::GetAllInfoTask : public DatabaseTask {
+ public:
+ explicit GetAllInfoTask(AppCacheStorageImpl* storage)
+ : DatabaseTask(storage),
+ info_collection_(new AppCacheInfoCollection()) {
+ }
+
+ // DatabaseTask:
+ virtual void Run() OVERRIDE;
+ virtual void RunCompleted() OVERRIDE;
+
+ protected:
+ virtual ~GetAllInfoTask() {}
+
+ private:
+ scoped_refptr<AppCacheInfoCollection> info_collection_;
+};
+
+void AppCacheStorageImpl::GetAllInfoTask::Run() {
+ std::set<GURL> origins;
+ database_->FindOriginsWithGroups(&origins);
+ for (std::set<GURL>::const_iterator origin = origins.begin();
+ origin != origins.end(); ++origin) {
+ AppCacheInfoVector& infos =
+ info_collection_->infos_by_origin[*origin];
+ std::vector<AppCacheDatabase::GroupRecord> groups;
+ database_->FindGroupsForOrigin(*origin, &groups);
+ for (std::vector<AppCacheDatabase::GroupRecord>::const_iterator
+ group = groups.begin();
+ group != groups.end(); ++group) {
+ AppCacheDatabase::CacheRecord cache_record;
+ database_->FindCacheForGroup(group->group_id, &cache_record);
+ AppCacheInfo info;
+ info.manifest_url = group->manifest_url;
+ info.creation_time = group->creation_time;
+ info.size = cache_record.cache_size;
+ info.last_access_time = group->last_access_time;
+ info.last_update_time = cache_record.update_time;
+ info.cache_id = cache_record.cache_id;
+ info.group_id = group->group_id;
+ info.is_complete = true;
+ infos.push_back(info);
+ }
+ }
+}
+
+void AppCacheStorageImpl::GetAllInfoTask::RunCompleted() {
+ DCHECK(delegates_.size() == 1);
+ FOR_EACH_DELEGATE(delegates_, OnAllInfo(info_collection_.get()));
+}
+
+// StoreOrLoadTask -------
+
+class AppCacheStorageImpl::StoreOrLoadTask : public DatabaseTask {
+ protected:
+ explicit StoreOrLoadTask(AppCacheStorageImpl* storage)
+ : DatabaseTask(storage) {}
+ virtual ~StoreOrLoadTask() {}
+
+ bool FindRelatedCacheRecords(int64 cache_id);
+ void CreateCacheAndGroupFromRecords(
+ scoped_refptr<AppCache>* cache, scoped_refptr<AppCacheGroup>* group);
+
+ AppCacheDatabase::GroupRecord group_record_;
+ AppCacheDatabase::CacheRecord cache_record_;
+ std::vector<AppCacheDatabase::EntryRecord> entry_records_;
+ std::vector<AppCacheDatabase::NamespaceRecord>
+ intercept_namespace_records_;
+ std::vector<AppCacheDatabase::NamespaceRecord>
+ fallback_namespace_records_;
+ std::vector<AppCacheDatabase::OnlineWhiteListRecord>
+ online_whitelist_records_;
+};
+
+bool AppCacheStorageImpl::StoreOrLoadTask::FindRelatedCacheRecords(
+ int64 cache_id) {
+ return database_->FindEntriesForCache(cache_id, &entry_records_) &&
+ database_->FindNamespacesForCache(
+ cache_id, &intercept_namespace_records_,
+ &fallback_namespace_records_) &&
+ database_->FindOnlineWhiteListForCache(
+ cache_id, &online_whitelist_records_);
+}
+
+void AppCacheStorageImpl::StoreOrLoadTask::CreateCacheAndGroupFromRecords(
+ scoped_refptr<AppCache>* cache, scoped_refptr<AppCacheGroup>* group) {
+ DCHECK(storage_ && cache && group);
+
+ (*cache) = storage_->working_set_.GetCache(cache_record_.cache_id);
+ if (cache->get()) {
+ (*group) = cache->get()->owning_group();
+ DCHECK(group->get());
+ DCHECK_EQ(group_record_.group_id, group->get()->group_id());
+
+ // TODO(michaeln): histogram is fishing for clues to crbug/95101
+ if (!cache->get()->GetEntry(group_record_.manifest_url)) {
+ AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
+ AppCacheHistograms::CALLSITE_0);
+ }
+
+ storage_->NotifyStorageAccessed(group_record_.origin);
+ return;
+ }
+
+ (*cache) = new AppCache(storage_, cache_record_.cache_id);
+ cache->get()->InitializeWithDatabaseRecords(
+ cache_record_, entry_records_,
+ intercept_namespace_records_,
+ fallback_namespace_records_,
+ online_whitelist_records_);
+ cache->get()->set_complete(true);
+
+ (*group) = storage_->working_set_.GetGroup(group_record_.manifest_url);
+ if (group->get()) {
+ DCHECK(group_record_.group_id == group->get()->group_id());
+ group->get()->AddCache(cache->get());
+
+ // TODO(michaeln): histogram is fishing for clues to crbug/95101
+ if (!cache->get()->GetEntry(group_record_.manifest_url)) {
+ AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
+ AppCacheHistograms::CALLSITE_1);
+ }
+ } else {
+ (*group) = new AppCacheGroup(
+ storage_, group_record_.manifest_url,
+ group_record_.group_id);
+ group->get()->set_creation_time(group_record_.creation_time);
+ group->get()->AddCache(cache->get());
+
+ // TODO(michaeln): histogram is fishing for clues to crbug/95101
+ if (!cache->get()->GetEntry(group_record_.manifest_url)) {
+ AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
+ AppCacheHistograms::CALLSITE_2);
+ }
+ }
+ DCHECK(group->get()->newest_complete_cache() == cache->get());
+
+ // We have to update foriegn entries if MarkEntryAsForeignTasks
+ // are in flight.
+ std::vector<GURL> urls;
+ storage_->GetPendingForeignMarkingsForCache(cache->get()->cache_id(), &urls);
+ for (std::vector<GURL>::iterator iter = urls.begin();
+ iter != urls.end(); ++iter) {
+ DCHECK(cache->get()->GetEntry(*iter));
+ cache->get()->GetEntry(*iter)->add_types(AppCacheEntry::FOREIGN);
+ }
+
+ storage_->NotifyStorageAccessed(group_record_.origin);
+
+ // TODO(michaeln): Maybe verify that the responses we expect to exist
+ // do actually exist in the disk_cache (and if not then what?)
+}
+
+// CacheLoadTask -------
+
+class AppCacheStorageImpl::CacheLoadTask : public StoreOrLoadTask {
+ public:
+ CacheLoadTask(int64 cache_id, AppCacheStorageImpl* storage)
+ : StoreOrLoadTask(storage), cache_id_(cache_id),
+ success_(false) {}
+
+ // DatabaseTask:
+ virtual void Run() OVERRIDE;
+ virtual void RunCompleted() OVERRIDE;
+
+ protected:
+ virtual ~CacheLoadTask() {}
+
+ private:
+ int64 cache_id_;
+ bool success_;
+};
+
+void AppCacheStorageImpl::CacheLoadTask::Run() {
+ success_ =
+ database_->FindCache(cache_id_, &cache_record_) &&
+ database_->FindGroup(cache_record_.group_id, &group_record_) &&
+ FindRelatedCacheRecords(cache_id_);
+
+ if (success_)
+ database_->UpdateGroupLastAccessTime(group_record_.group_id,
+ base::Time::Now());
+}
+
+void AppCacheStorageImpl::CacheLoadTask::RunCompleted() {
+ storage_->pending_cache_loads_.erase(cache_id_);
+ scoped_refptr<AppCache> cache;
+ scoped_refptr<AppCacheGroup> group;
+ if (success_ && !storage_->is_disabled()) {
+ DCHECK(cache_record_.cache_id == cache_id_);
+ CreateCacheAndGroupFromRecords(&cache, &group);
+ }
+ FOR_EACH_DELEGATE(delegates_, OnCacheLoaded(cache.get(), cache_id_));
+}
+
+// GroupLoadTask -------
+
+class AppCacheStorageImpl::GroupLoadTask : public StoreOrLoadTask {
+ public:
+ GroupLoadTask(GURL manifest_url, AppCacheStorageImpl* storage)
+ : StoreOrLoadTask(storage), manifest_url_(manifest_url),
+ success_(false) {}
+
+ // DatabaseTask:
+ virtual void Run() OVERRIDE;
+ virtual void RunCompleted() OVERRIDE;
+
+ protected:
+ virtual ~GroupLoadTask() {}
+
+ private:
+ GURL manifest_url_;
+ bool success_;
+};
+
+void AppCacheStorageImpl::GroupLoadTask::Run() {
+ success_ =
+ database_->FindGroupForManifestUrl(manifest_url_, &group_record_) &&
+ database_->FindCacheForGroup(group_record_.group_id, &cache_record_) &&
+ FindRelatedCacheRecords(cache_record_.cache_id);
+
+ if (success_)
+ database_->UpdateGroupLastAccessTime(group_record_.group_id,
+ base::Time::Now());
+}
+
+void AppCacheStorageImpl::GroupLoadTask::RunCompleted() {
+ storage_->pending_group_loads_.erase(manifest_url_);
+ scoped_refptr<AppCacheGroup> group;
+ scoped_refptr<AppCache> cache;
+ if (!storage_->is_disabled()) {
+ if (success_) {
+ DCHECK(group_record_.manifest_url == manifest_url_);
+ CreateCacheAndGroupFromRecords(&cache, &group);
+ } else {
+ group = storage_->working_set_.GetGroup(manifest_url_);
+ if (!group.get()) {
+ group =
+ new AppCacheGroup(storage_, manifest_url_, storage_->NewGroupId());
+ }
+ }
+ }
+ FOR_EACH_DELEGATE(delegates_, OnGroupLoaded(group.get(), manifest_url_));
+}
+
+// StoreGroupAndCacheTask -------
+
+class AppCacheStorageImpl::StoreGroupAndCacheTask : public StoreOrLoadTask {
+ public:
+ StoreGroupAndCacheTask(AppCacheStorageImpl* storage, AppCacheGroup* group,
+ AppCache* newest_cache);
+
+ void GetQuotaThenSchedule();
+ void OnQuotaCallback(
+ quota::QuotaStatusCode status, int64 usage, int64 quota);
+
+ // DatabaseTask:
+ virtual void Run() OVERRIDE;
+ virtual void RunCompleted() OVERRIDE;
+ virtual void CancelCompletion() OVERRIDE;
+
+ protected:
+ virtual ~StoreGroupAndCacheTask() {}
+
+ private:
+ scoped_refptr<AppCacheGroup> group_;
+ scoped_refptr<AppCache> cache_;
+ bool success_;
+ bool would_exceed_quota_;
+ int64 space_available_;
+ int64 new_origin_usage_;
+ std::vector<int64> newly_deletable_response_ids_;
+};
+
+AppCacheStorageImpl::StoreGroupAndCacheTask::StoreGroupAndCacheTask(
+ AppCacheStorageImpl* storage, AppCacheGroup* group, AppCache* newest_cache)
+ : StoreOrLoadTask(storage), group_(group), cache_(newest_cache),
+ success_(false), would_exceed_quota_(false),
+ space_available_(-1), new_origin_usage_(-1) {
+ group_record_.group_id = group->group_id();
+ group_record_.manifest_url = group->manifest_url();
+ group_record_.origin = group_record_.manifest_url.GetOrigin();
+ newest_cache->ToDatabaseRecords(
+ group,
+ &cache_record_, &entry_records_,
+ &intercept_namespace_records_,
+ &fallback_namespace_records_,
+ &online_whitelist_records_);
+}
+
+void AppCacheStorageImpl::StoreGroupAndCacheTask::GetQuotaThenSchedule() {
+ quota::QuotaManager* quota_manager = NULL;
+ if (storage_->service()->quota_manager_proxy()) {
+ quota_manager =
+ storage_->service()->quota_manager_proxy()->quota_manager();
+ }
+
+ if (!quota_manager) {
+ if (storage_->service()->special_storage_policy() &&
+ storage_->service()->special_storage_policy()->IsStorageUnlimited(
+ group_record_.origin))
+ space_available_ = kint64max;
+ Schedule();
+ return;
+ }
+
+ // We have to ask the quota manager for the value.
+ storage_->pending_quota_queries_.insert(this);
+ quota_manager->GetUsageAndQuota(
+ group_record_.origin, quota::kStorageTypeTemporary,
+ base::Bind(&StoreGroupAndCacheTask::OnQuotaCallback, this));
+}
+
+void AppCacheStorageImpl::StoreGroupAndCacheTask::OnQuotaCallback(
+ quota::QuotaStatusCode status, int64 usage, int64 quota) {
+ if (storage_) {
+ if (status == quota::kQuotaStatusOk)
+ space_available_ = std::max(static_cast<int64>(0), quota - usage);
+ else
+ space_available_ = 0;
+ storage_->pending_quota_queries_.erase(this);
+ Schedule();
+ }
+}
+
+void AppCacheStorageImpl::StoreGroupAndCacheTask::Run() {
+ DCHECK(!success_);
+ sql::Connection* connection = database_->db_connection();
+ if (!connection)
+ return;
+
+ sql::Transaction transaction(connection);
+ if (!transaction.Begin())
+ return;
+
+ int64 old_origin_usage = database_->GetOriginUsage(group_record_.origin);
+
+ AppCacheDatabase::GroupRecord existing_group;
+ success_ = database_->FindGroup(group_record_.group_id, &existing_group);
+ if (!success_) {
+ group_record_.creation_time = base::Time::Now();
+ group_record_.last_access_time = base::Time::Now();
+ success_ = database_->InsertGroup(&group_record_);
+ } else {
+ DCHECK(group_record_.group_id == existing_group.group_id);
+ DCHECK(group_record_.manifest_url == existing_group.manifest_url);
+ DCHECK(group_record_.origin == existing_group.origin);
+
+ database_->UpdateGroupLastAccessTime(group_record_.group_id,
+ base::Time::Now());
+
+ AppCacheDatabase::CacheRecord cache;
+ if (database_->FindCacheForGroup(group_record_.group_id, &cache)) {
+ // Get the set of response ids in the old cache.
+ std::set<int64> existing_response_ids;
+ database_->FindResponseIdsForCacheAsSet(cache.cache_id,
+ &existing_response_ids);
+
+ // Remove those that remain in the new cache.
+ std::vector<AppCacheDatabase::EntryRecord>::const_iterator entry_iter =
+ entry_records_.begin();
+ while (entry_iter != entry_records_.end()) {
+ existing_response_ids.erase(entry_iter->response_id);
+ ++entry_iter;
+ }
+
+ // The rest are deletable.
+ std::set<int64>::const_iterator id_iter = existing_response_ids.begin();
+ while (id_iter != existing_response_ids.end()) {
+ newly_deletable_response_ids_.push_back(*id_iter);
+ ++id_iter;
+ }
+
+ success_ =
+ database_->DeleteCache(cache.cache_id) &&
+ database_->DeleteEntriesForCache(cache.cache_id) &&
+ database_->DeleteNamespacesForCache(cache.cache_id) &&
+ database_->DeleteOnlineWhiteListForCache(cache.cache_id) &&
+ database_->InsertDeletableResponseIds(newly_deletable_response_ids_);
+ // TODO(michaeln): store group_id too with deletable ids
+ } else {
+ NOTREACHED() << "A existing group without a cache is unexpected";
+ }
+ }
+
+ success_ =
+ success_ &&
+ database_->InsertCache(&cache_record_) &&
+ database_->InsertEntryRecords(entry_records_) &&
+ database_->InsertNamespaceRecords(intercept_namespace_records_) &&
+ database_->InsertNamespaceRecords(fallback_namespace_records_) &&
+ database_->InsertOnlineWhiteListRecords(online_whitelist_records_);
+
+ if (!success_)
+ return;
+
+ new_origin_usage_ = database_->GetOriginUsage(group_record_.origin);
+
+ // Only check quota when the new usage exceeds the old usage.
+ if (new_origin_usage_ <= old_origin_usage) {
+ success_ = transaction.Commit();
+ return;
+ }
+
+ // Use a simple hard-coded value when not using quota management.
+ if (space_available_ == -1) {
+ if (new_origin_usage_ > kDefaultQuota) {
+ would_exceed_quota_ = true;
+ success_ = false;
+ return;
+ }
+ success_ = transaction.Commit();
+ return;
+ }
+
+ // Check limits based on the space availbable given to us via the
+ // quota system.
+ int64 delta = new_origin_usage_ - old_origin_usage;
+ if (delta > space_available_) {
+ would_exceed_quota_ = true;
+ success_ = false;
+ return;
+ }
+
+ success_ = transaction.Commit();
+}
+
+void AppCacheStorageImpl::StoreGroupAndCacheTask::RunCompleted() {
+ if (success_) {
+ storage_->UpdateUsageMapAndNotify(
+ group_->manifest_url().GetOrigin(), new_origin_usage_);
+ if (cache_.get() != group_->newest_complete_cache()) {
+ cache_->set_complete(true);
+ group_->AddCache(cache_.get());
+ }
+ if (group_->creation_time().is_null())
+ group_->set_creation_time(group_record_.creation_time);
+ group_->AddNewlyDeletableResponseIds(&newly_deletable_response_ids_);
+ }
+ FOR_EACH_DELEGATE(
+ delegates_,
+ OnGroupAndNewestCacheStored(
+ group_.get(), cache_.get(), success_, would_exceed_quota_));
+ group_ = NULL;
+ cache_ = NULL;
+
+ // TODO(michaeln): if (would_exceed_quota_) what if the current usage
+ // also exceeds the quota? http://crbug.com/83968
+}
+
+void AppCacheStorageImpl::StoreGroupAndCacheTask::CancelCompletion() {
+ // Overriden to safely drop our reference to the group and cache
+ // which are not thread safe refcounted.
+ DatabaseTask::CancelCompletion();
+ group_ = NULL;
+ cache_ = NULL;
+}
+
+// FindMainResponseTask -------
+
+// Helpers for FindMainResponseTask::Run()
+namespace {
+class SortByCachePreference
+ : public std::binary_function<
+ AppCacheDatabase::EntryRecord,
+ AppCacheDatabase::EntryRecord,
+ bool> {
+ public:
+ SortByCachePreference(int64 preferred_id, const std::set<int64>& in_use_ids)
+ : preferred_id_(preferred_id), in_use_ids_(in_use_ids) {
+ }
+ bool operator()(
+ const AppCacheDatabase::EntryRecord& lhs,
+ const AppCacheDatabase::EntryRecord& rhs) {
+ return compute_value(lhs) > compute_value(rhs);
+ }
+ private:
+ int compute_value(const AppCacheDatabase::EntryRecord& entry) {
+ if (entry.cache_id == preferred_id_)
+ return 100;
+ else if (in_use_ids_.find(entry.cache_id) != in_use_ids_.end())
+ return 50;
+ return 0;
+ }
+ int64 preferred_id_;
+ const std::set<int64>& in_use_ids_;
+};
+
+bool SortByLength(
+ const AppCacheDatabase::NamespaceRecord& lhs,
+ const AppCacheDatabase::NamespaceRecord& rhs) {
+ return lhs.namespace_.namespace_url.spec().length() >
+ rhs.namespace_.namespace_url.spec().length();
+}
+
+class NetworkNamespaceHelper {
+ public:
+ explicit NetworkNamespaceHelper(AppCacheDatabase* database)
+ : database_(database) {
+ }
+
+ bool IsInNetworkNamespace(const GURL& url, int64 cache_id) {
+ typedef std::pair<WhiteListMap::iterator, bool> InsertResult;
+ InsertResult result = namespaces_map_.insert(
+ WhiteListMap::value_type(cache_id, AppCacheNamespaceVector()));
+ if (result.second)
+ GetOnlineWhiteListForCache(cache_id, &result.first->second);
+ return AppCache::FindNamespace(result.first->second, url) != NULL;
+ }
+
+ private:
+ void GetOnlineWhiteListForCache(
+ int64 cache_id, AppCacheNamespaceVector* namespaces) {
+ DCHECK(namespaces && namespaces->empty());
+ typedef std::vector<AppCacheDatabase::OnlineWhiteListRecord>
+ WhiteListVector;
+ WhiteListVector records;
+ if (!database_->FindOnlineWhiteListForCache(cache_id, &records))
+ return;
+ WhiteListVector::const_iterator iter = records.begin();
+ while (iter != records.end()) {
+ namespaces->push_back(
+ AppCacheNamespace(APPCACHE_NETWORK_NAMESPACE, iter->namespace_url,
+ GURL(), iter->is_pattern));
+ ++iter;
+ }
+ }
+
+ // Key is cache id
+ typedef std::map<int64, AppCacheNamespaceVector> WhiteListMap;
+ WhiteListMap namespaces_map_;
+ AppCacheDatabase* database_;
+};
+
+} // namespace
+
+class AppCacheStorageImpl::FindMainResponseTask : public DatabaseTask {
+ public:
+ FindMainResponseTask(AppCacheStorageImpl* storage,
+ const GURL& url,
+ const GURL& preferred_manifest_url,
+ const AppCacheWorkingSet::GroupMap* groups_in_use)
+ : DatabaseTask(storage), url_(url),
+ preferred_manifest_url_(preferred_manifest_url),
+ cache_id_(kAppCacheNoCacheId), group_id_(0) {
+ if (groups_in_use) {
+ for (AppCacheWorkingSet::GroupMap::const_iterator it =
+ groups_in_use->begin();
+ it != groups_in_use->end(); ++it) {
+ AppCacheGroup* group = it->second;
+ AppCache* cache = group->newest_complete_cache();
+ if (group->is_obsolete() || !cache)
+ continue;
+ cache_ids_in_use_.insert(cache->cache_id());
+ }
+ }
+ }
+
+ // DatabaseTask:
+ virtual void Run() OVERRIDE;
+ virtual void RunCompleted() OVERRIDE;
+
+ protected:
+ virtual ~FindMainResponseTask() {}
+
+ private:
+ typedef std::vector<AppCacheDatabase::NamespaceRecord*>
+ NamespaceRecordPtrVector;
+
+ bool FindExactMatch(int64 preferred_id);
+ bool FindNamespaceMatch(int64 preferred_id);
+ bool FindNamespaceHelper(
+ int64 preferred_cache_id,
+ AppCacheDatabase::NamespaceRecordVector* namespaces,
+ NetworkNamespaceHelper* network_namespace_helper);
+ bool FindFirstValidNamespace(const NamespaceRecordPtrVector& namespaces);
+
+ GURL url_;
+ GURL preferred_manifest_url_;
+ std::set<int64> cache_ids_in_use_;
+ AppCacheEntry entry_;
+ AppCacheEntry fallback_entry_;
+ GURL namespace_entry_url_;
+ int64 cache_id_;
+ int64 group_id_;
+ GURL manifest_url_;
+};
+
+void AppCacheStorageImpl::FindMainResponseTask::Run() {
+ // NOTE: The heuristics around choosing amoungst multiple candidates
+ // is underspecified, and just plain not fully understood. This needs
+ // to be refined.
+
+ // The 'preferred_manifest_url' is the url of the manifest associated
+ // with the page that opened or embedded the page being loaded now.
+ // We have a strong preference to use resources from that cache.
+ // We also have a lesser bias to use resources from caches that are currently
+ // being used by other unrelated pages.
+ // TODO(michaeln): come up with a 'preferred_manifest_url' in more cases
+ // - when navigating a frame whose current contents are from an appcache
+ // - when clicking an href in a frame that is appcached
+ int64 preferred_cache_id = kAppCacheNoCacheId;
+ if (!preferred_manifest_url_.is_empty()) {
+ AppCacheDatabase::GroupRecord preferred_group;
+ AppCacheDatabase::CacheRecord preferred_cache;
+ if (database_->FindGroupForManifestUrl(
+ preferred_manifest_url_, &preferred_group) &&
+ database_->FindCacheForGroup(
+ preferred_group.group_id, &preferred_cache)) {
+ preferred_cache_id = preferred_cache.cache_id;
+ }
+ }
+
+ if (FindExactMatch(preferred_cache_id) ||
+ FindNamespaceMatch(preferred_cache_id)) {
+ // We found something.
+ DCHECK(cache_id_ != kAppCacheNoCacheId && !manifest_url_.is_empty() &&
+ group_id_ != 0);
+ return;
+ }
+
+ // We didn't find anything.
+ DCHECK(cache_id_ == kAppCacheNoCacheId && manifest_url_.is_empty() &&
+ group_id_ == 0);
+}
+
+bool AppCacheStorageImpl::
+FindMainResponseTask::FindExactMatch(int64 preferred_cache_id) {
+ std::vector<AppCacheDatabase::EntryRecord> entries;
+ if (database_->FindEntriesForUrl(url_, &entries) && !entries.empty()) {
+ // Sort them in order of preference, from the preferred_cache first,
+ // followed by hits from caches that are 'in use', then the rest.
+ std::sort(entries.begin(), entries.end(),
+ SortByCachePreference(preferred_cache_id, cache_ids_in_use_));
+
+ // Take the first with a valid, non-foreign entry.
+ std::vector<AppCacheDatabase::EntryRecord>::iterator iter;
+ for (iter = entries.begin(); iter < entries.end(); ++iter) {
+ AppCacheDatabase::GroupRecord group_record;
+ if ((iter->flags & AppCacheEntry::FOREIGN) ||
+ !database_->FindGroupForCache(iter->cache_id, &group_record)) {
+ continue;
+ }
+ manifest_url_ = group_record.manifest_url;
+ group_id_ = group_record.group_id;
+ entry_ = AppCacheEntry(iter->flags, iter->response_id);
+ cache_id_ = iter->cache_id;
+ return true; // We found an exact match.
+ }
+ }
+ return false;
+}
+
+bool AppCacheStorageImpl::
+FindMainResponseTask::FindNamespaceMatch(int64 preferred_cache_id) {
+ AppCacheDatabase::NamespaceRecordVector all_intercepts;
+ AppCacheDatabase::NamespaceRecordVector all_fallbacks;
+ if (!database_->FindNamespacesForOrigin(
+ url_.GetOrigin(), &all_intercepts, &all_fallbacks)
+ || (all_intercepts.empty() && all_fallbacks.empty())) {
+ return false;
+ }
+
+ NetworkNamespaceHelper network_namespace_helper(database_);
+ if (FindNamespaceHelper(preferred_cache_id,
+ &all_intercepts,
+ &network_namespace_helper) ||
+ FindNamespaceHelper(preferred_cache_id,
+ &all_fallbacks,
+ &network_namespace_helper)) {
+ return true;
+ }
+ return false;
+}
+
+bool AppCacheStorageImpl::
+FindMainResponseTask::FindNamespaceHelper(
+ int64 preferred_cache_id,
+ AppCacheDatabase::NamespaceRecordVector* namespaces,
+ NetworkNamespaceHelper* network_namespace_helper) {
+ // Sort them by length, longer matches within the same cache/bucket take
+ // precedence.
+ std::sort(namespaces->begin(), namespaces->end(), SortByLength);
+
+ NamespaceRecordPtrVector preferred_namespaces;
+ NamespaceRecordPtrVector inuse_namespaces;
+ NamespaceRecordPtrVector other_namespaces;
+ std::vector<AppCacheDatabase::NamespaceRecord>::iterator iter;
+ for (iter = namespaces->begin(); iter < namespaces->end(); ++iter) {
+ // Skip those that aren't a match.
+ if (!iter->namespace_.IsMatch(url_))
+ continue;
+
+ // Skip namespaces where the requested url falls into a network
+ // namespace of its containing appcache.
+ if (network_namespace_helper->IsInNetworkNamespace(url_, iter->cache_id))
+ continue;
+
+ // Bin them into one of our three buckets.
+ if (iter->cache_id == preferred_cache_id)
+ preferred_namespaces.push_back(&(*iter));
+ else if (cache_ids_in_use_.find(iter->cache_id) != cache_ids_in_use_.end())
+ inuse_namespaces.push_back(&(*iter));
+ else
+ other_namespaces.push_back(&(*iter));
+ }
+
+ if (FindFirstValidNamespace(preferred_namespaces) ||
+ FindFirstValidNamespace(inuse_namespaces) ||
+ FindFirstValidNamespace(other_namespaces))
+ return true; // We found one.
+
+ // We didn't find anything.
+ return false;
+}
+
+bool AppCacheStorageImpl::
+FindMainResponseTask::FindFirstValidNamespace(
+ const NamespaceRecordPtrVector& namespaces) {
+ // Take the first with a valid, non-foreign entry.
+ NamespaceRecordPtrVector::const_iterator iter;
+ for (iter = namespaces.begin(); iter < namespaces.end(); ++iter) {
+ AppCacheDatabase::EntryRecord entry_record;
+ if (database_->FindEntry((*iter)->cache_id, (*iter)->namespace_.target_url,
+ &entry_record)) {
+ AppCacheDatabase::GroupRecord group_record;
+ if ((entry_record.flags & AppCacheEntry::FOREIGN) ||
+ !database_->FindGroupForCache(entry_record.cache_id, &group_record)) {
+ continue;
+ }
+ manifest_url_ = group_record.manifest_url;
+ group_id_ = group_record.group_id;
+ cache_id_ = (*iter)->cache_id;
+ namespace_entry_url_ = (*iter)->namespace_.target_url;
+ if ((*iter)->namespace_.type == APPCACHE_FALLBACK_NAMESPACE)
+ fallback_entry_ = AppCacheEntry(entry_record.flags,
+ entry_record.response_id);
+ else
+ entry_ = AppCacheEntry(entry_record.flags, entry_record.response_id);
+ return true; // We found one.
+ }
+ }
+ return false; // We didn't find a match.
+}
+
+void AppCacheStorageImpl::FindMainResponseTask::RunCompleted() {
+ storage_->CallOnMainResponseFound(
+ &delegates_, url_, entry_, namespace_entry_url_, fallback_entry_,
+ cache_id_, group_id_, manifest_url_);
+}
+
+// MarkEntryAsForeignTask -------
+
+class AppCacheStorageImpl::MarkEntryAsForeignTask : public DatabaseTask {
+ public:
+ MarkEntryAsForeignTask(
+ AppCacheStorageImpl* storage, const GURL& url, int64 cache_id)
+ : DatabaseTask(storage), cache_id_(cache_id), entry_url_(url) {}
+
+ // DatabaseTask:
+ virtual void Run() OVERRIDE;
+ virtual void RunCompleted() OVERRIDE;
+
+ protected:
+ virtual ~MarkEntryAsForeignTask() {}
+
+ private:
+ int64 cache_id_;
+ GURL entry_url_;
+};
+
+void AppCacheStorageImpl::MarkEntryAsForeignTask::Run() {
+ database_->AddEntryFlags(entry_url_, cache_id_, AppCacheEntry::FOREIGN);
+}
+
+void AppCacheStorageImpl::MarkEntryAsForeignTask::RunCompleted() {
+ DCHECK(storage_->pending_foreign_markings_.front().first == entry_url_ &&
+ storage_->pending_foreign_markings_.front().second == cache_id_);
+ storage_->pending_foreign_markings_.pop_front();
+}
+
+// MakeGroupObsoleteTask -------
+
+class AppCacheStorageImpl::MakeGroupObsoleteTask : public DatabaseTask {
+ public:
+ MakeGroupObsoleteTask(AppCacheStorageImpl* storage,
+ AppCacheGroup* group,
+ int response_code);
+
+ // DatabaseTask:
+ virtual void Run() OVERRIDE;
+ virtual void RunCompleted() OVERRIDE;
+ virtual void CancelCompletion() OVERRIDE;
+
+ protected:
+ virtual ~MakeGroupObsoleteTask() {}
+
+ private:
+ scoped_refptr<AppCacheGroup> group_;
+ int64 group_id_;
+ GURL origin_;
+ bool success_;
+ int response_code_;
+ int64 new_origin_usage_;
+ std::vector<int64> newly_deletable_response_ids_;
+};
+
+AppCacheStorageImpl::MakeGroupObsoleteTask::MakeGroupObsoleteTask(
+ AppCacheStorageImpl* storage,
+ AppCacheGroup* group,
+ int response_code)
+ : DatabaseTask(storage),
+ group_(group),
+ group_id_(group->group_id()),
+ origin_(group->manifest_url().GetOrigin()),
+ success_(false),
+ response_code_(response_code),
+ new_origin_usage_(-1) {}
+
+void AppCacheStorageImpl::MakeGroupObsoleteTask::Run() {
+ DCHECK(!success_);
+ sql::Connection* connection = database_->db_connection();
+ if (!connection)
+ return;
+
+ sql::Transaction transaction(connection);
+ if (!transaction.Begin())
+ return;
+
+ AppCacheDatabase::GroupRecord group_record;
+ if (!database_->FindGroup(group_id_, &group_record)) {
+ // This group doesn't exists in the database, nothing todo here.
+ new_origin_usage_ = database_->GetOriginUsage(origin_);
+ success_ = true;
+ return;
+ }
+
+ DCHECK_EQ(group_record.origin, origin_);
+ success_ = DeleteGroupAndRelatedRecords(database_,
+ group_id_,
+ &newly_deletable_response_ids_);
+
+ new_origin_usage_ = database_->GetOriginUsage(origin_);
+ success_ = success_ && transaction.Commit();
+}
+
+void AppCacheStorageImpl::MakeGroupObsoleteTask::RunCompleted() {
+ if (success_) {
+ group_->set_obsolete(true);
+ if (!storage_->is_disabled()) {
+ storage_->UpdateUsageMapAndNotify(origin_, new_origin_usage_);
+ group_->AddNewlyDeletableResponseIds(&newly_deletable_response_ids_);
+
+ // Also remove from the working set, caches for an 'obsolete' group
+ // may linger in use, but the group itself cannot be looked up by
+ // 'manifest_url' in the working set any longer.
+ storage_->working_set()->RemoveGroup(group_.get());
+ }
+ }
+ FOR_EACH_DELEGATE(
+ delegates_, OnGroupMadeObsolete(group_.get(), success_, response_code_));
+ group_ = NULL;
+}
+
+void AppCacheStorageImpl::MakeGroupObsoleteTask::CancelCompletion() {
+ // Overriden to safely drop our reference to the group
+ // which is not thread safe refcounted.
+ DatabaseTask::CancelCompletion();
+ group_ = NULL;
+}
+
+// GetDeletableResponseIdsTask -------
+
+class AppCacheStorageImpl::GetDeletableResponseIdsTask : public DatabaseTask {
+ public:
+ GetDeletableResponseIdsTask(AppCacheStorageImpl* storage, int64 max_rowid)
+ : DatabaseTask(storage), max_rowid_(max_rowid) {}
+
+ // DatabaseTask:
+ virtual void Run() OVERRIDE;
+ virtual void RunCompleted() OVERRIDE;
+
+ protected:
+ virtual ~GetDeletableResponseIdsTask() {}
+
+ private:
+ int64 max_rowid_;
+ std::vector<int64> response_ids_;
+};
+
+void AppCacheStorageImpl::GetDeletableResponseIdsTask::Run() {
+ const int kSqlLimit = 1000;
+ database_->GetDeletableResponseIds(&response_ids_, max_rowid_, kSqlLimit);
+ // TODO(michaeln): retrieve group_ids too
+}
+
+void AppCacheStorageImpl::GetDeletableResponseIdsTask::RunCompleted() {
+ if (!response_ids_.empty())
+ storage_->StartDeletingResponses(response_ids_);
+}
+
+// InsertDeletableResponseIdsTask -------
+
+class AppCacheStorageImpl::InsertDeletableResponseIdsTask
+ : public DatabaseTask {
+ public:
+ explicit InsertDeletableResponseIdsTask(AppCacheStorageImpl* storage)
+ : DatabaseTask(storage) {}
+
+ // DatabaseTask:
+ virtual void Run() OVERRIDE;
+
+ std::vector<int64> response_ids_;
+
+ protected:
+ virtual ~InsertDeletableResponseIdsTask() {}
+};
+
+void AppCacheStorageImpl::InsertDeletableResponseIdsTask::Run() {
+ database_->InsertDeletableResponseIds(response_ids_);
+ // TODO(michaeln): store group_ids too
+}
+
+// DeleteDeletableResponseIdsTask -------
+
+class AppCacheStorageImpl::DeleteDeletableResponseIdsTask
+ : public DatabaseTask {
+ public:
+ explicit DeleteDeletableResponseIdsTask(AppCacheStorageImpl* storage)
+ : DatabaseTask(storage) {}
+
+ // DatabaseTask:
+ virtual void Run() OVERRIDE;
+
+ std::vector<int64> response_ids_;
+
+ protected:
+ virtual ~DeleteDeletableResponseIdsTask() {}
+};
+
+void AppCacheStorageImpl::DeleteDeletableResponseIdsTask::Run() {
+ database_->DeleteDeletableResponseIds(response_ids_);
+}
+
+// UpdateGroupLastAccessTimeTask -------
+
+class AppCacheStorageImpl::UpdateGroupLastAccessTimeTask
+ : public DatabaseTask {
+ public:
+ UpdateGroupLastAccessTimeTask(
+ AppCacheStorageImpl* storage, AppCacheGroup* group, base::Time time)
+ : DatabaseTask(storage), group_id_(group->group_id()),
+ last_access_time_(time) {
+ storage->NotifyStorageAccessed(group->manifest_url().GetOrigin());
+ }
+
+ // DatabaseTask:
+ virtual void Run() OVERRIDE;
+
+ protected:
+ virtual ~UpdateGroupLastAccessTimeTask() {}
+
+ private:
+ int64 group_id_;
+ base::Time last_access_time_;
+};
+
+void AppCacheStorageImpl::UpdateGroupLastAccessTimeTask::Run() {
+ database_->UpdateGroupLastAccessTime(group_id_, last_access_time_);
+}
+
+
+// AppCacheStorageImpl ---------------------------------------------------
+
+AppCacheStorageImpl::AppCacheStorageImpl(AppCacheServiceImpl* service)
+ : AppCacheStorage(service),
+ is_incognito_(false),
+ is_response_deletion_scheduled_(false),
+ did_start_deleting_responses_(false),
+ last_deletable_response_rowid_(0),
+ database_(NULL),
+ is_disabled_(false),
+ weak_factory_(this) {
+}
+
+AppCacheStorageImpl::~AppCacheStorageImpl() {
+ std::for_each(pending_quota_queries_.begin(),
+ pending_quota_queries_.end(),
+ std::mem_fun(&DatabaseTask::CancelCompletion));
+ std::for_each(scheduled_database_tasks_.begin(),
+ scheduled_database_tasks_.end(),
+ std::mem_fun(&DatabaseTask::CancelCompletion));
+
+ if (database_ &&
+ !db_thread_->PostTask(
+ FROM_HERE,
+ base::Bind(&ClearSessionOnlyOrigins, database_,
+ make_scoped_refptr(service_->special_storage_policy()),
+ service()->force_keep_session_state()))) {
+ delete database_;
+ }
+ database_ = NULL; // So no further database tasks can be scheduled.
+}
+
+void AppCacheStorageImpl::Initialize(const base::FilePath& cache_directory,
+ base::MessageLoopProxy* db_thread,
+ base::MessageLoopProxy* cache_thread) {
+ DCHECK(db_thread);
+
+ cache_directory_ = cache_directory;
+ is_incognito_ = cache_directory_.empty();
+
+ base::FilePath db_file_path;
+ if (!is_incognito_)
+ db_file_path = cache_directory_.Append(kAppCacheDatabaseName);
+ database_ = new AppCacheDatabase(db_file_path);
+
+ db_thread_ = db_thread;
+ cache_thread_ = cache_thread;
+
+ scoped_refptr<InitTask> task(new InitTask(this));
+ task->Schedule();
+}
+
+void AppCacheStorageImpl::Disable() {
+ if (is_disabled_)
+ return;
+ VLOG(1) << "Disabling appcache storage.";
+ is_disabled_ = true;
+ ClearUsageMapAndNotify();
+ working_set()->Disable();
+ if (disk_cache_)
+ disk_cache_->Disable();
+ scoped_refptr<DisableDatabaseTask> task(new DisableDatabaseTask(this));
+ task->Schedule();
+}
+
+void AppCacheStorageImpl::GetAllInfo(Delegate* delegate) {
+ DCHECK(delegate);
+ scoped_refptr<GetAllInfoTask> task(new GetAllInfoTask(this));
+ task->AddDelegate(GetOrCreateDelegateReference(delegate));
+ task->Schedule();
+}
+
+void AppCacheStorageImpl::LoadCache(int64 id, Delegate* delegate) {
+ DCHECK(delegate);
+ if (is_disabled_) {
+ delegate->OnCacheLoaded(NULL, id);
+ return;
+ }
+
+ AppCache* cache = working_set_.GetCache(id);
+ if (cache) {
+ delegate->OnCacheLoaded(cache, id);
+ if (cache->owning_group()) {
+ scoped_refptr<DatabaseTask> update_task(
+ new UpdateGroupLastAccessTimeTask(
+ this, cache->owning_group(), base::Time::Now()));
+ update_task->Schedule();
+ }
+ return;
+ }
+ scoped_refptr<CacheLoadTask> task(GetPendingCacheLoadTask(id));
+ if (task.get()) {
+ task->AddDelegate(GetOrCreateDelegateReference(delegate));
+ return;
+ }
+ task = new CacheLoadTask(id, this);
+ task->AddDelegate(GetOrCreateDelegateReference(delegate));
+ task->Schedule();
+ pending_cache_loads_[id] = task.get();
+}
+
+void AppCacheStorageImpl::LoadOrCreateGroup(
+ const GURL& manifest_url, Delegate* delegate) {
+ DCHECK(delegate);
+ if (is_disabled_) {
+ delegate->OnGroupLoaded(NULL, manifest_url);
+ return;
+ }
+
+ AppCacheGroup* group = working_set_.GetGroup(manifest_url);
+ if (group) {
+ delegate->OnGroupLoaded(group, manifest_url);
+ scoped_refptr<DatabaseTask> update_task(
+ new UpdateGroupLastAccessTimeTask(
+ this, group, base::Time::Now()));
+ update_task->Schedule();
+ return;
+ }
+
+ scoped_refptr<GroupLoadTask> task(GetPendingGroupLoadTask(manifest_url));
+ if (task.get()) {
+ task->AddDelegate(GetOrCreateDelegateReference(delegate));
+ return;
+ }
+
+ if (usage_map_.find(manifest_url.GetOrigin()) == usage_map_.end()) {
+ // No need to query the database, return a new group immediately.
+ scoped_refptr<AppCacheGroup> group(new AppCacheGroup(
+ this, manifest_url, NewGroupId()));
+ delegate->OnGroupLoaded(group.get(), manifest_url);
+ return;
+ }
+
+ task = new GroupLoadTask(manifest_url, this);
+ task->AddDelegate(GetOrCreateDelegateReference(delegate));
+ task->Schedule();
+ pending_group_loads_[manifest_url] = task.get();
+}
+
+void AppCacheStorageImpl::StoreGroupAndNewestCache(
+ AppCacheGroup* group, AppCache* newest_cache, Delegate* delegate) {
+ // TODO(michaeln): distinguish between a simple update of an existing
+ // cache that just adds new master entry(s), and the insertion of a
+ // whole new cache. The StoreGroupAndCacheTask as written will handle
+ // the simple update case in a very heavy weight way (delete all and
+ // the reinsert all over again).
+ DCHECK(group && delegate && newest_cache);
+ scoped_refptr<StoreGroupAndCacheTask> task(
+ new StoreGroupAndCacheTask(this, group, newest_cache));
+ task->AddDelegate(GetOrCreateDelegateReference(delegate));
+ task->GetQuotaThenSchedule();
+
+ // TODO(michaeln): histogram is fishing for clues to crbug/95101
+ if (!newest_cache->GetEntry(group->manifest_url())) {
+ AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
+ AppCacheHistograms::CALLSITE_3);
+ }
+}
+
+void AppCacheStorageImpl::FindResponseForMainRequest(
+ const GURL& url, const GURL& preferred_manifest_url,
+ Delegate* delegate) {
+ DCHECK(delegate);
+
+ const GURL* url_ptr = &url;
+ GURL url_no_ref;
+ if (url.has_ref()) {
+ GURL::Replacements replacements;
+ replacements.ClearRef();
+ url_no_ref = url.ReplaceComponents(replacements);
+ url_ptr = &url_no_ref;
+ }
+
+ const GURL origin = url.GetOrigin();
+
+ // First look in our working set for a direct hit without having to query
+ // the database.
+ const AppCacheWorkingSet::GroupMap* groups_in_use =
+ working_set()->GetGroupsInOrigin(origin);
+ if (groups_in_use) {
+ if (!preferred_manifest_url.is_empty()) {
+ AppCacheWorkingSet::GroupMap::const_iterator found =
+ groups_in_use->find(preferred_manifest_url);
+ if (found != groups_in_use->end() &&
+ FindResponseForMainRequestInGroup(
+ found->second, *url_ptr, delegate)) {
+ return;
+ }
+ } else {
+ for (AppCacheWorkingSet::GroupMap::const_iterator it =
+ groups_in_use->begin();
+ it != groups_in_use->end(); ++it) {
+ if (FindResponseForMainRequestInGroup(
+ it->second, *url_ptr, delegate)) {
+ return;
+ }
+ }
+ }
+ }
+
+ if (IsInitTaskComplete() && usage_map_.find(origin) == usage_map_.end()) {
+ // No need to query the database, return async'ly but without going thru
+ // the DB thread.
+ scoped_refptr<AppCacheGroup> no_group;
+ scoped_refptr<AppCache> no_cache;
+ ScheduleSimpleTask(
+ base::Bind(&AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse,
+ weak_factory_.GetWeakPtr(), url, AppCacheEntry(), no_group,
+ no_cache,
+ make_scoped_refptr(GetOrCreateDelegateReference(delegate))));
+ return;
+ }
+
+ // We have to query the database, schedule a database task to do so.
+ scoped_refptr<FindMainResponseTask> task(
+ new FindMainResponseTask(this, *url_ptr, preferred_manifest_url,
+ groups_in_use));
+ task->AddDelegate(GetOrCreateDelegateReference(delegate));
+ task->Schedule();
+}
+
+bool AppCacheStorageImpl::FindResponseForMainRequestInGroup(
+ AppCacheGroup* group, const GURL& url, Delegate* delegate) {
+ AppCache* cache = group->newest_complete_cache();
+ if (group->is_obsolete() || !cache)
+ return false;
+
+ AppCacheEntry* entry = cache->GetEntry(url);
+ if (!entry || entry->IsForeign())
+ return false;
+
+ ScheduleSimpleTask(
+ base::Bind(&AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse,
+ weak_factory_.GetWeakPtr(), url, *entry,
+ make_scoped_refptr(group), make_scoped_refptr(cache),
+ make_scoped_refptr(GetOrCreateDelegateReference(delegate))));
+ return true;
+}
+
+void AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse(
+ const GURL& url,
+ const AppCacheEntry& found_entry,
+ scoped_refptr<AppCacheGroup> group,
+ scoped_refptr<AppCache> cache,
+ scoped_refptr<DelegateReference> delegate_ref) {
+ if (delegate_ref->delegate) {
+ DelegateReferenceVector delegates(1, delegate_ref);
+ CallOnMainResponseFound(
+ &delegates, url, found_entry,
+ GURL(), AppCacheEntry(),
+ cache.get() ? cache->cache_id() : kAppCacheNoCacheId,
+ group.get() ? group->group_id() : kAppCacheNoCacheId,
+ group.get() ? group->manifest_url() : GURL());
+ }
+}
+
+void AppCacheStorageImpl::CallOnMainResponseFound(
+ DelegateReferenceVector* delegates,
+ const GURL& url, const AppCacheEntry& entry,
+ const GURL& namespace_entry_url, const AppCacheEntry& fallback_entry,
+ int64 cache_id, int64 group_id, const GURL& manifest_url) {
+ FOR_EACH_DELEGATE(
+ (*delegates),
+ OnMainResponseFound(url, entry,
+ namespace_entry_url, fallback_entry,
+ cache_id, group_id, manifest_url));
+}
+
+void AppCacheStorageImpl::FindResponseForSubRequest(
+ AppCache* cache, const GURL& url,
+ AppCacheEntry* found_entry, AppCacheEntry* found_fallback_entry,
+ bool* found_network_namespace) {
+ DCHECK(cache && cache->is_complete());
+
+ // When a group is forcibly deleted, all subresource loads for pages
+ // using caches in the group will result in a synthesized network errors.
+ // Forcible deletion is not a function that is covered by the HTML5 spec.
+ if (cache->owning_group()->is_being_deleted()) {
+ *found_entry = AppCacheEntry();
+ *found_fallback_entry = AppCacheEntry();
+ *found_network_namespace = false;
+ return;
+ }
+
+ GURL fallback_namespace_not_used;
+ GURL intercept_namespace_not_used;
+ cache->FindResponseForRequest(
+ url, found_entry, &intercept_namespace_not_used,
+ found_fallback_entry, &fallback_namespace_not_used,
+ found_network_namespace);
+}
+
+void AppCacheStorageImpl::MarkEntryAsForeign(
+ const GURL& entry_url, int64 cache_id) {
+ AppCache* cache = working_set_.GetCache(cache_id);
+ if (cache) {
+ AppCacheEntry* entry = cache->GetEntry(entry_url);
+ DCHECK(entry);
+ if (entry)
+ entry->add_types(AppCacheEntry::FOREIGN);
+ }
+ scoped_refptr<MarkEntryAsForeignTask> task(
+ new MarkEntryAsForeignTask(this, entry_url, cache_id));
+ task->Schedule();
+ pending_foreign_markings_.push_back(std::make_pair(entry_url, cache_id));
+}
+
+void AppCacheStorageImpl::MakeGroupObsolete(AppCacheGroup* group,
+ Delegate* delegate,
+ int response_code) {
+ DCHECK(group && delegate);
+ scoped_refptr<MakeGroupObsoleteTask> task(
+ new MakeGroupObsoleteTask(this, group, response_code));
+ task->AddDelegate(GetOrCreateDelegateReference(delegate));
+ task->Schedule();
+}
+
+AppCacheResponseReader* AppCacheStorageImpl::CreateResponseReader(
+ const GURL& manifest_url, int64 group_id, int64 response_id) {
+ return new AppCacheResponseReader(response_id, group_id, disk_cache());
+}
+
+AppCacheResponseWriter* AppCacheStorageImpl::CreateResponseWriter(
+ const GURL& manifest_url, int64 group_id) {
+ return new AppCacheResponseWriter(NewResponseId(), group_id, disk_cache());
+}
+
+void AppCacheStorageImpl::DoomResponses(
+ const GURL& manifest_url, const std::vector<int64>& response_ids) {
+ if (response_ids.empty())
+ return;
+
+ // Start deleting them from the disk cache lazily.
+ StartDeletingResponses(response_ids);
+
+ // Also schedule a database task to record these ids in the
+ // deletable responses table.
+ // TODO(michaeln): There is a race here. If the browser crashes
+ // prior to committing these rows to the database and prior to us
+ // having deleted them from the disk cache, we'll never delete them.
+ scoped_refptr<InsertDeletableResponseIdsTask> task(
+ new InsertDeletableResponseIdsTask(this));
+ task->response_ids_ = response_ids;
+ task->Schedule();
+}
+
+void AppCacheStorageImpl::DeleteResponses(
+ const GURL& manifest_url, const std::vector<int64>& response_ids) {
+ if (response_ids.empty())
+ return;
+ StartDeletingResponses(response_ids);
+}
+
+void AppCacheStorageImpl::DelayedStartDeletingUnusedResponses() {
+ // Only if we haven't already begun.
+ if (!did_start_deleting_responses_) {
+ scoped_refptr<GetDeletableResponseIdsTask> task(
+ new GetDeletableResponseIdsTask(this, last_deletable_response_rowid_));
+ task->Schedule();
+ }
+}
+
+void AppCacheStorageImpl::StartDeletingResponses(
+ const std::vector<int64>& response_ids) {
+ DCHECK(!response_ids.empty());
+ did_start_deleting_responses_ = true;
+ deletable_response_ids_.insert(
+ deletable_response_ids_.end(),
+ response_ids.begin(), response_ids.end());
+ if (!is_response_deletion_scheduled_)
+ ScheduleDeleteOneResponse();
+}
+
+void AppCacheStorageImpl::ScheduleDeleteOneResponse() {
+ DCHECK(!is_response_deletion_scheduled_);
+ const base::TimeDelta kDelay = base::TimeDelta::FromMilliseconds(10);
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&AppCacheStorageImpl::DeleteOneResponse,
+ weak_factory_.GetWeakPtr()),
+ kDelay);
+ is_response_deletion_scheduled_ = true;
+}
+
+void AppCacheStorageImpl::DeleteOneResponse() {
+ DCHECK(is_response_deletion_scheduled_);
+ DCHECK(!deletable_response_ids_.empty());
+
+ if (!disk_cache()) {
+ DCHECK(is_disabled_);
+ deletable_response_ids_.clear();
+ deleted_response_ids_.clear();
+ is_response_deletion_scheduled_ = false;
+ return;
+ }
+
+ // TODO(michaeln): add group_id to DoomEntry args
+ int64 id = deletable_response_ids_.front();
+ int rv = disk_cache_->DoomEntry(
+ id, base::Bind(&AppCacheStorageImpl::OnDeletedOneResponse,
+ base::Unretained(this)));
+ if (rv != net::ERR_IO_PENDING)
+ OnDeletedOneResponse(rv);
+}
+
+void AppCacheStorageImpl::OnDeletedOneResponse(int rv) {
+ is_response_deletion_scheduled_ = false;
+ if (is_disabled_)
+ return;
+
+ int64 id = deletable_response_ids_.front();
+ deletable_response_ids_.pop_front();
+ if (rv != net::ERR_ABORTED)
+ deleted_response_ids_.push_back(id);
+
+ const size_t kBatchSize = 50U;
+ if (deleted_response_ids_.size() >= kBatchSize ||
+ deletable_response_ids_.empty()) {
+ scoped_refptr<DeleteDeletableResponseIdsTask> task(
+ new DeleteDeletableResponseIdsTask(this));
+ task->response_ids_.swap(deleted_response_ids_);
+ task->Schedule();
+ }
+
+ if (deletable_response_ids_.empty()) {
+ scoped_refptr<GetDeletableResponseIdsTask> task(
+ new GetDeletableResponseIdsTask(this, last_deletable_response_rowid_));
+ task->Schedule();
+ return;
+ }
+
+ ScheduleDeleteOneResponse();
+}
+
+AppCacheStorageImpl::CacheLoadTask*
+AppCacheStorageImpl::GetPendingCacheLoadTask(int64 cache_id) {
+ PendingCacheLoads::iterator found = pending_cache_loads_.find(cache_id);
+ if (found != pending_cache_loads_.end())
+ return found->second;
+ return NULL;
+}
+
+AppCacheStorageImpl::GroupLoadTask*
+AppCacheStorageImpl::GetPendingGroupLoadTask(const GURL& manifest_url) {
+ PendingGroupLoads::iterator found = pending_group_loads_.find(manifest_url);
+ if (found != pending_group_loads_.end())
+ return found->second;
+ return NULL;
+}
+
+void AppCacheStorageImpl::GetPendingForeignMarkingsForCache(
+ int64 cache_id, std::vector<GURL>* urls) {
+ PendingForeignMarkings::iterator iter = pending_foreign_markings_.begin();
+ while (iter != pending_foreign_markings_.end()) {
+ if (iter->second == cache_id)
+ urls->push_back(iter->first);
+ ++iter;
+ }
+}
+
+void AppCacheStorageImpl::ScheduleSimpleTask(const base::Closure& task) {
+ pending_simple_tasks_.push_back(task);
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&AppCacheStorageImpl::RunOnePendingSimpleTask,
+ weak_factory_.GetWeakPtr()));
+}
+
+void AppCacheStorageImpl::RunOnePendingSimpleTask() {
+ DCHECK(!pending_simple_tasks_.empty());
+ base::Closure task = pending_simple_tasks_.front();
+ pending_simple_tasks_.pop_front();
+ task.Run();
+}
+
+AppCacheDiskCache* AppCacheStorageImpl::disk_cache() {
+ DCHECK(IsInitTaskComplete());
+
+ if (is_disabled_)
+ return NULL;
+
+ if (!disk_cache_) {
+ int rv = net::OK;
+ disk_cache_.reset(new AppCacheDiskCache);
+ if (is_incognito_) {
+ rv = disk_cache_->InitWithMemBackend(
+ kMaxMemDiskCacheSize,
+ base::Bind(&AppCacheStorageImpl::OnDiskCacheInitialized,
+ base::Unretained(this)));
+ } else {
+ rv = disk_cache_->InitWithDiskBackend(
+ cache_directory_.Append(kDiskCacheDirectoryName),
+ kMaxDiskCacheSize,
+ false,
+ cache_thread_.get(),
+ base::Bind(&AppCacheStorageImpl::OnDiskCacheInitialized,
+ base::Unretained(this)));
+ }
+
+ if (rv != net::ERR_IO_PENDING)
+ OnDiskCacheInitialized(rv);
+ }
+ return disk_cache_.get();
+}
+
+void AppCacheStorageImpl::OnDiskCacheInitialized(int rv) {
+ if (rv != net::OK) {
+ LOG(ERROR) << "Failed to open the appcache diskcache.";
+ AppCacheHistograms::CountInitResult(AppCacheHistograms::DISK_CACHE_ERROR);
+
+ // We're unable to open the disk cache, this is a fatal error that we can't
+ // really recover from. We handle it by temporarily disabling the appcache
+ // deleting the directory on disk and reinitializing the appcache system.
+ Disable();
+ if (rv != net::ERR_ABORTED)
+ DeleteAndStartOver();
+ }
+}
+
+void AppCacheStorageImpl::DeleteAndStartOver() {
+ DCHECK(is_disabled_);
+ if (!is_incognito_) {
+ VLOG(1) << "Deleting existing appcache data and starting over.";
+ // We can have tasks in flight to close file handles on both the db
+ // and cache threads, we need to allow those tasks to cycle thru
+ // prior to deleting the files and calling reinit.
+ cache_thread_->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&base::DoNothing),
+ base::Bind(&AppCacheStorageImpl::DeleteAndStartOverPart2,
+ weak_factory_.GetWeakPtr()));
+ }
+}
+
+void AppCacheStorageImpl::DeleteAndStartOverPart2() {
+ db_thread_->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(base::IgnoreResult(&base::DeleteFile),
+ cache_directory_, true),
+ base::Bind(&AppCacheStorageImpl::CallScheduleReinitialize,
+ weak_factory_.GetWeakPtr()));
+}
+
+void AppCacheStorageImpl::CallScheduleReinitialize() {
+ service_->ScheduleReinitialize();
+ // note: 'this' may be deleted at this point.
+}
+
+} // namespace content
diff --git a/content/browser/appcache/appcache_storage_impl.h b/content/browser/appcache/appcache_storage_impl.h
new file mode 100644
index 0000000..dadf72e
--- /dev/null
+++ b/content/browser/appcache/appcache_storage_impl.h
@@ -0,0 +1,181 @@
+// Copyright (c) 2011 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.
+
+#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_STORAGE_IMPL_H_
+#define CONTENT_BROWSER_APPCACHE_APPCACHE_STORAGE_IMPL_H_
+
+#include <deque>
+#include <map>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "content/browser/appcache/appcache_database.h"
+#include "content/browser/appcache/appcache_disk_cache.h"
+#include "content/browser/appcache/appcache_storage.h"
+#include "content/common/content_export.h"
+
+namespace content {
+class AppCacheStorageImplTest;
+class ChromeAppCacheServiceTest;
+}
+
+namespace content {
+
+class AppCacheStorageImpl : public AppCacheStorage {
+ public:
+ explicit AppCacheStorageImpl(AppCacheServiceImpl* service);
+ virtual ~AppCacheStorageImpl();
+
+ void Initialize(const base::FilePath& cache_directory,
+ base::MessageLoopProxy* db_thread,
+ base::MessageLoopProxy* cache_thread);
+ void Disable();
+ bool is_disabled() const { return is_disabled_; }
+
+ // AppCacheStorage methods, see the base class for doc comments.
+ virtual void GetAllInfo(Delegate* delegate) OVERRIDE;
+ virtual void LoadCache(int64 id, Delegate* delegate) OVERRIDE;
+ virtual void LoadOrCreateGroup(const GURL& manifest_url,
+ Delegate* delegate) OVERRIDE;
+ virtual void StoreGroupAndNewestCache(AppCacheGroup* group,
+ AppCache* newest_cache,
+ Delegate* delegate) OVERRIDE;
+ virtual void FindResponseForMainRequest(const GURL& url,
+ const GURL& preferred_manifest_url,
+ Delegate* delegate) OVERRIDE;
+ virtual void FindResponseForSubRequest(
+ AppCache* cache, const GURL& url,
+ AppCacheEntry* found_entry, AppCacheEntry* found_fallback_entry,
+ bool* found_network_namespace) OVERRIDE;
+ virtual void MarkEntryAsForeign(const GURL& entry_url,
+ int64 cache_id) OVERRIDE;
+ virtual void MakeGroupObsolete(AppCacheGroup* group,
+ Delegate* delegate,
+ int response_code) OVERRIDE;
+ virtual AppCacheResponseReader* CreateResponseReader(
+ const GURL& manifest_url, int64 group_id, int64 response_id) OVERRIDE;
+ virtual AppCacheResponseWriter* CreateResponseWriter(
+ const GURL& manifest_url, int64 group_id) OVERRIDE;
+ virtual void DoomResponses(const GURL& manifest_url,
+ const std::vector<int64>& response_ids) OVERRIDE;
+ virtual void DeleteResponses(const GURL& manifest_url,
+ const std::vector<int64>& response_ids) OVERRIDE;
+
+ private:
+ // The AppCacheStorageImpl class methods and datamembers may only be
+ // accessed on the IO thread. This class manufactures seperate DatabaseTasks
+ // which access the DB on a seperate background thread.
+ class DatabaseTask;
+ class InitTask;
+ class DisableDatabaseTask;
+ class GetAllInfoTask;
+ class StoreOrLoadTask;
+ class CacheLoadTask;
+ class GroupLoadTask;
+ class StoreGroupAndCacheTask;
+ class FindMainResponseTask;
+ class MarkEntryAsForeignTask;
+ class MakeGroupObsoleteTask;
+ class GetDeletableResponseIdsTask;
+ class InsertDeletableResponseIdsTask;
+ class DeleteDeletableResponseIdsTask;
+ class UpdateGroupLastAccessTimeTask;
+
+ typedef std::deque<DatabaseTask*> DatabaseTaskQueue;
+ typedef std::map<int64, CacheLoadTask*> PendingCacheLoads;
+ typedef std::map<GURL, GroupLoadTask*> PendingGroupLoads;
+ typedef std::deque<std::pair<GURL, int64> > PendingForeignMarkings;
+ typedef std::set<StoreGroupAndCacheTask*> PendingQuotaQueries;
+
+ bool IsInitTaskComplete() {
+ return last_cache_id_ != AppCacheStorage::kUnitializedId;
+ }
+
+ CacheLoadTask* GetPendingCacheLoadTask(int64 cache_id);
+ GroupLoadTask* GetPendingGroupLoadTask(const GURL& manifest_url);
+ void GetPendingForeignMarkingsForCache(
+ int64 cache_id, std::vector<GURL>* urls);
+
+ void ScheduleSimpleTask(const base::Closure& task);
+ void RunOnePendingSimpleTask();
+
+ void DelayedStartDeletingUnusedResponses();
+ void StartDeletingResponses(const std::vector<int64>& response_ids);
+ void ScheduleDeleteOneResponse();
+ void DeleteOneResponse();
+
+ void OnDeletedOneResponse(int rv);
+ void OnDiskCacheInitialized(int rv);
+ void DeleteAndStartOver();
+ void DeleteAndStartOverPart2();
+ void CallScheduleReinitialize();
+
+ // Sometimes we can respond without having to query the database.
+ bool FindResponseForMainRequestInGroup(
+ AppCacheGroup* group, const GURL& url, Delegate* delegate);
+ void DeliverShortCircuitedFindMainResponse(
+ const GURL& url,
+ const AppCacheEntry& found_entry,
+ scoped_refptr<AppCacheGroup> group,
+ scoped_refptr<AppCache> newest_cache,
+ scoped_refptr<DelegateReference> delegate_ref);
+
+ void CallOnMainResponseFound(
+ DelegateReferenceVector* delegates,
+ const GURL& url, const AppCacheEntry& entry,
+ const GURL& namespace_entry_url, const AppCacheEntry& fallback_entry,
+ int64 cache_id, int64 group_id, const GURL& manifest_url);
+
+ CONTENT_EXPORT AppCacheDiskCache* disk_cache();
+
+ // The directory in which we place files in the file system.
+ base::FilePath cache_directory_;
+ bool is_incognito_;
+
+ // This class operates primarily on the IO thread, but schedules
+ // its DatabaseTasks on the db thread. Separately, the disk_cache uses
+ // the cache_thread.
+ scoped_refptr<base::MessageLoopProxy> db_thread_;
+ scoped_refptr<base::MessageLoopProxy> cache_thread_;
+
+ // Structures to keep track of DatabaseTasks that are in-flight.
+ DatabaseTaskQueue scheduled_database_tasks_;
+ PendingCacheLoads pending_cache_loads_;
+ PendingGroupLoads pending_group_loads_;
+ PendingForeignMarkings pending_foreign_markings_;
+ PendingQuotaQueries pending_quota_queries_;
+
+ // Structures to keep track of lazy response deletion.
+ std::deque<int64> deletable_response_ids_;
+ std::vector<int64> deleted_response_ids_;
+ bool is_response_deletion_scheduled_;
+ bool did_start_deleting_responses_;
+ int64 last_deletable_response_rowid_;
+
+ // Created on the IO thread, but only used on the DB thread.
+ AppCacheDatabase* database_;
+
+ // Set if we discover a fatal error like a corrupt SQL database or
+ // disk cache and cannot continue.
+ bool is_disabled_;
+
+ scoped_ptr<AppCacheDiskCache> disk_cache_;
+
+ // Used to short-circuit certain operations without having to schedule
+ // any tasks on the background database thread.
+ std::deque<base::Closure> pending_simple_tasks_;
+ base::WeakPtrFactory<AppCacheStorageImpl> weak_factory_;
+
+ friend class content::AppCacheStorageImplTest;
+ friend class content::ChromeAppCacheServiceTest;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_APPCACHE_APPCACHE_STORAGE_IMPL_H_
diff --git a/content/browser/appcache/appcache_storage_impl_unittest.cc b/content/browser/appcache/appcache_storage_impl_unittest.cc
index 4c467a1..a8489af 100644
--- a/content/browser/appcache/appcache_storage_impl_unittest.cc
+++ b/content/browser/appcache/appcache_storage_impl_unittest.cc
@@ -13,7 +13,16 @@
#include "base/message_loop/message_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
+#include "content/browser/appcache/appcache.h"
+#include "content/browser/appcache/appcache_backend_impl.h"
+#include "content/browser/appcache/appcache_database.h"
+#include "content/browser/appcache/appcache_entry.h"
+#include "content/browser/appcache/appcache_group.h"
+#include "content/browser/appcache/appcache_host.h"
#include "content/browser/appcache/appcache_interceptor.h"
+#include "content/browser/appcache/appcache_request_handler.h"
+#include "content/browser/appcache/appcache_service_impl.h"
+#include "content/browser/appcache/appcache_storage_impl.h"
#include "net/base/net_errors.h"
#include "net/base/request_priority.h"
#include "net/http/http_response_headers.h"
@@ -23,40 +32,8 @@
#include "net/url_request/url_request_test_util.h"
#include "sql/test/test_helpers.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/appcache/appcache.h"
-#include "webkit/browser/appcache/appcache_backend_impl.h"
-#include "webkit/browser/appcache/appcache_database.h"
-#include "webkit/browser/appcache/appcache_entry.h"
-#include "webkit/browser/appcache/appcache_group.h"
-#include "webkit/browser/appcache/appcache_host.h"
-#include "webkit/browser/appcache/appcache_request_handler.h"
-#include "webkit/browser/appcache/appcache_service_impl.h"
-#include "webkit/browser/appcache/appcache_storage_impl.h"
#include "webkit/browser/quota/quota_manager.h"
-using appcache::APPCACHE_FALLBACK_NAMESPACE;
-using appcache::APPCACHE_NETWORK_NAMESPACE;
-using appcache::AppCacheBackendImpl;
-using appcache::AppCacheDatabase;
-using appcache::AppCacheEntry;
-using appcache::AppCacheFrontend;
-using appcache::AppCacheHost;
-using appcache::AppCacheInfo;
-using appcache::AppCacheGroup;
-using appcache::AppCacheServiceImpl;
-using appcache::AppCacheStorage;
-using appcache::AppCacheStorageImpl;
-using appcache::AppCacheStorageReference;
-using appcache::AppCache;
-using appcache::AppCacheErrorDetails;
-using appcache::AppCacheEventID;
-using appcache::kAppCacheNoCacheId;
-using appcache::kAppCacheNoResponseId;
-using appcache::APPCACHE_INTERCEPT_NAMESPACE;
-using appcache::AppCacheLogLevel;
-using appcache::Namespace;
-using appcache::AppCacheStatus;
-
namespace content {
namespace {
@@ -1054,12 +1031,12 @@ class AppCacheStorageImplTest : public testing::Test {
cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::FALLBACK, 1));
cache_->AddEntry(kEntryUrl2, AppCacheEntry(AppCacheEntry::FALLBACK, 2));
cache_->fallback_namespaces_.push_back(
- Namespace(APPCACHE_FALLBACK_NAMESPACE,
+ AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE,
kFallbackNamespace2,
kEntryUrl2,
false));
cache_->fallback_namespaces_.push_back(
- Namespace(APPCACHE_FALLBACK_NAMESPACE,
+ AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE,
kFallbackNamespace,
kEntryUrl,
false));
@@ -1132,10 +1109,10 @@ class AppCacheStorageImplTest : public testing::Test {
cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::INTERCEPT, 1));
cache_->AddEntry(kEntryUrl2, AppCacheEntry(AppCacheEntry::INTERCEPT, 2));
cache_->intercept_namespaces_.push_back(
- Namespace(APPCACHE_INTERCEPT_NAMESPACE, kInterceptNamespace2,
+ AppCacheNamespace(APPCACHE_INTERCEPT_NAMESPACE, kInterceptNamespace2,
kEntryUrl2, false));
cache_->intercept_namespaces_.push_back(
- Namespace(APPCACHE_INTERCEPT_NAMESPACE, kInterceptNamespace,
+ AppCacheNamespace(APPCACHE_INTERCEPT_NAMESPACE, kInterceptNamespace,
kEntryUrl, false));
AppCacheDatabase::CacheRecord cache_record;
std::vector<AppCacheDatabase::EntryRecord> entries;
@@ -1202,8 +1179,8 @@ class AppCacheStorageImplTest : public testing::Test {
MakeCacheAndGroup(kManifestUrl, 2, 1, true);
cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::INTERCEPT, 1));
cache_->intercept_namespaces_.push_back(
- Namespace(APPCACHE_INTERCEPT_NAMESPACE, kInterceptPatternNamespace,
- kEntryUrl, true));
+ AppCacheNamespace(APPCACHE_INTERCEPT_NAMESPACE,
+ kInterceptPatternNamespace, kEntryUrl, true));
AppCacheDatabase::CacheRecord cache_record;
std::vector<AppCacheDatabase::EntryRecord> entries;
std::vector<AppCacheDatabase::NamespaceRecord> intercepts;
@@ -1289,8 +1266,8 @@ class AppCacheStorageImplTest : public testing::Test {
MakeCacheAndGroup(kManifestUrl, 2, 1, true);
cache_->AddEntry(kEntryUrl, AppCacheEntry(AppCacheEntry::FALLBACK, 1));
cache_->fallback_namespaces_.push_back(
- Namespace(APPCACHE_FALLBACK_NAMESPACE, kFallbackPatternNamespace,
- kEntryUrl, true));
+ AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE,
+ kFallbackPatternNamespace, kEntryUrl, true));
AppCacheDatabase::CacheRecord cache_record;
std::vector<AppCacheDatabase::EntryRecord> entries;
std::vector<AppCacheDatabase::NamespaceRecord> intercepts;
@@ -1420,7 +1397,7 @@ class AppCacheStorageImplTest : public testing::Test {
fallback_namespace_record.origin = manifest_url.GetOrigin();
EXPECT_TRUE(database()->InsertNamespace(&fallback_namespace_record));
cache_->fallback_namespaces_.push_back(
- Namespace(APPCACHE_FALLBACK_NAMESPACE,
+ AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE,
kFallbackNamespace,
kEntryUrl2,
false));
@@ -1535,16 +1512,16 @@ class AppCacheStorageImplTest : public testing::Test {
AppCacheEntry(AppCacheEntry::EXPLICIT | AppCacheEntry::FOREIGN, 1));
cache_->AddEntry(kEntryUrl2, AppCacheEntry(AppCacheEntry::FALLBACK, 2));
cache_->fallback_namespaces_.push_back(
- Namespace(APPCACHE_FALLBACK_NAMESPACE,
+ AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE,
kFallbackNamespace,
kEntryUrl2,
false));
cache_->online_whitelist_namespaces_.push_back(
- Namespace(APPCACHE_NETWORK_NAMESPACE, kOnlineNamespace,
+ AppCacheNamespace(APPCACHE_NETWORK_NAMESPACE, kOnlineNamespace,
GURL(), false));
cache_->online_whitelist_namespaces_.push_back(
- Namespace(APPCACHE_NETWORK_NAMESPACE, kOnlineNamespaceWithinFallback,
- GURL(), false));
+ AppCacheNamespace(APPCACHE_NETWORK_NAMESPACE,
+ kOnlineNamespaceWithinFallback, GURL(), false));
AppCacheDatabase::EntryRecord entry_record;
entry_record.cache_id = 1;
diff --git a/content/browser/appcache/appcache_storage_unittest.cc b/content/browser/appcache/appcache_storage_unittest.cc
index fa69696..827fce6 100644
--- a/content/browser/appcache/appcache_storage_unittest.cc
+++ b/content/browser/appcache/appcache_storage_unittest.cc
@@ -3,19 +3,13 @@
// found in the LICENSE file.
#include "base/message_loop/message_loop.h"
+#include "content/browser/appcache/appcache.h"
+#include "content/browser/appcache/appcache_group.h"
+#include "content/browser/appcache/appcache_response.h"
+#include "content/browser/appcache/appcache_storage.h"
#include "content/browser/appcache/mock_appcache_service.h"
#include "content/browser/quota/mock_quota_manager_proxy.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/appcache/appcache.h"
-#include "webkit/browser/appcache/appcache_group.h"
-#include "webkit/browser/appcache/appcache_response.h"
-#include "webkit/browser/appcache/appcache_storage.h"
-
-using appcache::AppCache;
-using appcache::AppCacheGroup;
-using appcache::AppCacheResponseInfo;
-using appcache::AppCacheStorage;
-using appcache::kUnkownResponseDataSize;
namespace content {
diff --git a/content/browser/appcache/appcache_unittest.cc b/content/browser/appcache/appcache_unittest.cc
index 32c4e89..a82ab3d 100644
--- a/content/browser/appcache/appcache_unittest.cc
+++ b/content/browser/appcache/appcache_unittest.cc
@@ -2,30 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "content/browser/appcache/appcache.h"
+#include "content/browser/appcache/appcache_host.h"
#include "content/browser/appcache/mock_appcache_service.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/appcache/appcache.h"
-#include "webkit/browser/appcache/appcache_host.h"
-
-using appcache::AppCache;
-using appcache::AppCacheDatabase;
-using appcache::AppCacheEntry;
-using appcache::AppCacheFrontend;
-using appcache::AppCacheGroup;
-using appcache::AppCacheHost;
-using appcache::AppCacheInfo;
-using appcache::AppCacheErrorDetails;
-using appcache::AppCacheEventID;
-using appcache::APPCACHE_FALLBACK_NAMESPACE;
-using appcache::APPCACHE_INTERCEPT_NAMESPACE;
-using appcache::AppCacheLogLevel;
-using appcache::Manifest;
-using appcache::Namespace;
-using appcache::NamespaceVector;
-using appcache::APPCACHE_NETWORK_NAMESPACE;
-using appcache::PARSE_MANIFEST_ALLOWING_INTERCEPTS;
-using appcache::PARSE_MANIFEST_PER_STANDARD;
-using appcache::AppCacheStatus;
namespace content {
@@ -132,23 +112,26 @@ TEST(AppCacheTest, InitializeWithManifest) {
manifest.explicit_urls.insert("http://one.com");
manifest.explicit_urls.insert("http://two.com");
manifest.fallback_namespaces.push_back(
- Namespace(APPCACHE_FALLBACK_NAMESPACE, GURL("http://fb1.com"),
+ AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE, GURL("http://fb1.com"),
GURL("http://fbone.com"), true));
manifest.online_whitelist_namespaces.push_back(
- Namespace(APPCACHE_NETWORK_NAMESPACE, GURL("http://w1.com"), GURL(), false));
+ AppCacheNamespace(APPCACHE_NETWORK_NAMESPACE, GURL("http://w1.com"),
+ GURL(), false));
manifest.online_whitelist_namespaces.push_back(
- Namespace(APPCACHE_NETWORK_NAMESPACE, GURL("http://w2.com"), GURL(), false));
+ AppCacheNamespace(APPCACHE_NETWORK_NAMESPACE, GURL("http://w2.com"),
+ GURL(), false));
manifest.online_whitelist_all = true;
cache->InitializeWithManifest(&manifest);
- const std::vector<Namespace>& fallbacks =
+ const std::vector<AppCacheNamespace>& fallbacks =
cache->fallback_namespaces_;
size_t expected = 1;
EXPECT_EQ(expected, fallbacks.size());
EXPECT_EQ(GURL("http://fb1.com"), fallbacks[0].namespace_url);
EXPECT_EQ(GURL("http://fbone.com"), fallbacks[0].target_url);
EXPECT_TRUE(fallbacks[0].is_pattern);
- const NamespaceVector& whitelist = cache->online_whitelist_namespaces_;
+ const AppCacheNamespaceVector& whitelist =
+ cache->online_whitelist_namespaces_;
expected = 2;
EXPECT_EQ(expected, whitelist.size());
EXPECT_EQ(GURL("http://w1.com"), whitelist[0].namespace_url);
@@ -193,24 +176,23 @@ TEST(AppCacheTest, FindResponseForRequest) {
Manifest manifest;
manifest.online_whitelist_namespaces.push_back(
- Namespace(APPCACHE_NETWORK_NAMESPACE, kOnlineNamespaceUrl,
- GURL(), false));
+ AppCacheNamespace(APPCACHE_NETWORK_NAMESPACE, kOnlineNamespaceUrl,
+ GURL(), false));
manifest.online_whitelist_namespaces.push_back(
- Namespace(APPCACHE_NETWORK_NAMESPACE,
- kOnlineNamespaceWithinOtherNamespaces,
- GURL(), false));
+ AppCacheNamespace(APPCACHE_NETWORK_NAMESPACE,
+ kOnlineNamespaceWithinOtherNamespaces, GURL(), false));
manifest.fallback_namespaces.push_back(
- Namespace(APPCACHE_FALLBACK_NAMESPACE, kFallbackNamespaceUrl1,
- kFallbackEntryUrl1, false));
+ AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE, kFallbackNamespaceUrl1,
+ kFallbackEntryUrl1, false));
manifest.fallback_namespaces.push_back(
- Namespace(APPCACHE_FALLBACK_NAMESPACE, kFallbackNamespaceUrl2,
- kFallbackEntryUrl2, false));
+ AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE, kFallbackNamespaceUrl2,
+ kFallbackEntryUrl2, false));
manifest.intercept_namespaces.push_back(
- Namespace(APPCACHE_INTERCEPT_NAMESPACE, kInterceptNamespace,
- kInterceptNamespaceEntry, false));
+ AppCacheNamespace(APPCACHE_INTERCEPT_NAMESPACE, kInterceptNamespace,
+ kInterceptNamespaceEntry, false));
manifest.intercept_namespaces.push_back(
- Namespace(APPCACHE_INTERCEPT_NAMESPACE, kInterceptNamespaceWithinFallback,
- kInterceptNamespaceEntry, false));
+ AppCacheNamespace(APPCACHE_INTERCEPT_NAMESPACE,
+ kInterceptNamespaceWithinFallback, kInterceptNamespaceEntry, false));
// Create a cache with some namespaces and entries.
scoped_refptr<AppCache> cache(new AppCache(service.storage(), 1234));
@@ -385,8 +367,8 @@ TEST(AppCacheTest, FindInterceptPatternResponseForRequest) {
const int64 kInterceptResponseId = 1;
Manifest manifest;
manifest.intercept_namespaces.push_back(
- Namespace(APPCACHE_INTERCEPT_NAMESPACE, kInterceptPatternNamespace,
- kInterceptNamespaceEntry, true));
+ AppCacheNamespace(APPCACHE_INTERCEPT_NAMESPACE,
+ kInterceptPatternNamespace, kInterceptNamespaceEntry, true));
scoped_refptr<AppCache> cache(new AppCache(service.storage(), 1234));
cache->InitializeWithManifest(&manifest);
cache->AddEntry(
@@ -456,7 +438,7 @@ TEST(AppCacheTest, FindFallbackPatternResponseForRequest) {
const int64 kFallbackResponseId = 1;
Manifest manifest;
manifest.fallback_namespaces.push_back(
- Namespace(APPCACHE_FALLBACK_NAMESPACE, kFallbackPatternNamespace,
+ AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE, kFallbackPatternNamespace,
kFallbackNamespaceEntry, true));
scoped_refptr<AppCache> cache(new AppCache(service.storage(), 1234));
cache->InitializeWithManifest(&manifest);
@@ -526,7 +508,7 @@ TEST(AppCacheTest, FindNetworkNamespacePatternResponseForRequest) {
kNetworkNamespaceBase.Resolve("*.hit*"));
Manifest manifest;
manifest.online_whitelist_namespaces.push_back(
- Namespace(APPCACHE_NETWORK_NAMESPACE, kNetworkPatternNamespace,
+ AppCacheNamespace(APPCACHE_NETWORK_NAMESPACE, kNetworkPatternNamespace,
GURL(), true));
manifest.online_whitelist_all = false;
scoped_refptr<AppCache> cache(new AppCache(service.storage(), 1234));
@@ -644,7 +626,7 @@ TEST(AppCacheTest, ToFromDatabaseRecords) {
}
TEST(AppCacheTest, IsNamespaceMatch) {
- Namespace prefix;
+ AppCacheNamespace prefix;
prefix.namespace_url = GURL("http://foo.com/prefix");
prefix.is_pattern = false;
EXPECT_TRUE(prefix.IsMatch(
@@ -652,7 +634,7 @@ TEST(AppCacheTest, IsNamespaceMatch) {
EXPECT_FALSE(prefix.IsMatch(
GURL("http://foo.com/nope")));
- Namespace bar_no_star;
+ AppCacheNamespace bar_no_star;
bar_no_star.namespace_url = GURL("http://foo.com/bar");
bar_no_star.is_pattern = true;
EXPECT_TRUE(bar_no_star.IsMatch(
@@ -660,7 +642,7 @@ TEST(AppCacheTest, IsNamespaceMatch) {
EXPECT_FALSE(bar_no_star.IsMatch(
GURL("http://foo.com/bar/nope")));
- Namespace bar_star;
+ AppCacheNamespace bar_star;
bar_star.namespace_url = GURL("http://foo.com/bar/*");
bar_star.is_pattern = true;
EXPECT_TRUE(bar_star.IsMatch(
@@ -670,7 +652,7 @@ TEST(AppCacheTest, IsNamespaceMatch) {
EXPECT_FALSE(bar_star.IsMatch(
GURL("http://foo.com/not_bar/should_not_match")));
- Namespace star_bar_star;
+ AppCacheNamespace star_bar_star;
star_bar_star.namespace_url = GURL("http://foo.com/*/bar/*");
star_bar_star.is_pattern = true;
EXPECT_TRUE(star_bar_star.IsMatch(
@@ -680,7 +662,7 @@ TEST(AppCacheTest, IsNamespaceMatch) {
EXPECT_FALSE(star_bar_star.IsMatch(
GURL("http://foo.com/any/not_bar/no_match")));
- Namespace query_star_edit;
+ AppCacheNamespace query_star_edit;
query_star_edit.namespace_url = GURL("http://foo.com/query?id=*&verb=edit*");
query_star_edit.is_pattern = true;
EXPECT_TRUE(query_star_edit.IsMatch(
@@ -692,7 +674,7 @@ TEST(AppCacheTest, IsNamespaceMatch) {
EXPECT_TRUE(query_star_edit.IsMatch(
GURL("http://foo.com/query?id=123&verb=print&verb=edit")));
- Namespace star_greediness;
+ AppCacheNamespace star_greediness;
star_greediness.namespace_url = GURL("http://foo.com/*/b");
star_greediness.is_pattern = true;
EXPECT_TRUE(star_greediness.IsMatch(
diff --git a/content/browser/appcache/appcache_update_job.cc b/content/browser/appcache/appcache_update_job.cc
new file mode 100644
index 0000000..347d4b5c
--- /dev/null
+++ b/content/browser/appcache/appcache_update_job.cc
@@ -0,0 +1,1611 @@
+// Copyright (c) 2012 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 "content/browser/appcache/appcache_update_job.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/compiler_specific.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "content/browser/appcache/appcache_group.h"
+#include "content/browser/appcache/appcache_histograms.h"
+#include "net/base/io_buffer.h"
+#include "net/base/load_flags.h"
+#include "net/base/net_errors.h"
+#include "net/base/request_priority.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_request_context.h"
+
+namespace content {
+
+static const int kBufferSize = 32768;
+static const size_t kMaxConcurrentUrlFetches = 2;
+static const int kMax503Retries = 3;
+
+static std::string FormatUrlErrorMessage(
+ const char* format, const GURL& url,
+ AppCacheUpdateJob::ResultType error,
+ int response_code) {
+ // Show the net response code if we have one.
+ int code = response_code;
+ if (error != AppCacheUpdateJob::SERVER_ERROR)
+ code = static_cast<int>(error);
+ return base::StringPrintf(format, code, url.spec().c_str());
+}
+
+// Helper class for collecting hosts per frontend when sending notifications
+// so that only one notification is sent for all hosts using the same frontend.
+class HostNotifier {
+ public:
+ typedef std::vector<int> HostIds;
+ typedef std::map<AppCacheFrontend*, HostIds> NotifyHostMap;
+
+ // Caller is responsible for ensuring there will be no duplicate hosts.
+ void AddHost(AppCacheHost* host) {
+ std::pair<NotifyHostMap::iterator , bool> ret = hosts_to_notify.insert(
+ NotifyHostMap::value_type(host->frontend(), HostIds()));
+ ret.first->second.push_back(host->host_id());
+ }
+
+ void AddHosts(const std::set<AppCacheHost*>& hosts) {
+ for (std::set<AppCacheHost*>::const_iterator it = hosts.begin();
+ it != hosts.end(); ++it) {
+ AddHost(*it);
+ }
+ }
+
+ void SendNotifications(AppCacheEventID event_id) {
+ for (NotifyHostMap::iterator it = hosts_to_notify.begin();
+ it != hosts_to_notify.end(); ++it) {
+ AppCacheFrontend* frontend = it->first;
+ frontend->OnEventRaised(it->second, event_id);
+ }
+ }
+
+ void SendProgressNotifications(
+ const GURL& url, int num_total, int num_complete) {
+ for (NotifyHostMap::iterator it = hosts_to_notify.begin();
+ it != hosts_to_notify.end(); ++it) {
+ AppCacheFrontend* frontend = it->first;
+ frontend->OnProgressEventRaised(it->second, url,
+ num_total, num_complete);
+ }
+ }
+
+ void SendErrorNotifications(const AppCacheErrorDetails& details) {
+ DCHECK(!details.message.empty());
+ for (NotifyHostMap::iterator it = hosts_to_notify.begin();
+ it != hosts_to_notify.end(); ++it) {
+ AppCacheFrontend* frontend = it->first;
+ frontend->OnErrorEventRaised(it->second, details);
+ }
+ }
+
+ void SendLogMessage(const std::string& message) {
+ for (NotifyHostMap::iterator it = hosts_to_notify.begin();
+ it != hosts_to_notify.end(); ++it) {
+ AppCacheFrontend* frontend = it->first;
+ for (HostIds::iterator id = it->second.begin();
+ id != it->second.end(); ++id) {
+ frontend->OnLogMessage(*id, APPCACHE_LOG_WARNING, message);
+ }
+ }
+ }
+
+ private:
+ NotifyHostMap hosts_to_notify;
+};
+
+AppCacheUpdateJob::UrlToFetch::UrlToFetch(const GURL& url,
+ bool checked,
+ AppCacheResponseInfo* info)
+ : url(url),
+ storage_checked(checked),
+ existing_response_info(info) {
+}
+
+AppCacheUpdateJob::UrlToFetch::~UrlToFetch() {
+}
+
+// Helper class to fetch resources. Depending on the fetch type,
+// can either fetch to an in-memory string or write the response
+// data out to the disk cache.
+AppCacheUpdateJob::URLFetcher::URLFetcher(const GURL& url,
+ FetchType fetch_type,
+ AppCacheUpdateJob* job)
+ : url_(url),
+ job_(job),
+ fetch_type_(fetch_type),
+ retry_503_attempts_(0),
+ buffer_(new net::IOBuffer(kBufferSize)),
+ request_(job->service_->request_context()
+ ->CreateRequest(url, net::DEFAULT_PRIORITY, this, NULL)),
+ result_(UPDATE_OK),
+ redirect_response_code_(-1) {}
+
+AppCacheUpdateJob::URLFetcher::~URLFetcher() {
+}
+
+void AppCacheUpdateJob::URLFetcher::Start() {
+ request_->set_first_party_for_cookies(job_->manifest_url_);
+ request_->SetLoadFlags(request_->load_flags() |
+ net::LOAD_DISABLE_INTERCEPT);
+ if (existing_response_headers_.get())
+ AddConditionalHeaders(existing_response_headers_.get());
+ request_->Start();
+}
+
+void AppCacheUpdateJob::URLFetcher::OnReceivedRedirect(
+ net::URLRequest* request, const GURL& new_url, bool* defer_redirect) {
+ DCHECK(request_ == request);
+ // Redirect is not allowed by the update process.
+ job_->MadeProgress();
+ redirect_response_code_ = request->GetResponseCode();
+ request->Cancel();
+ result_ = REDIRECT_ERROR;
+ OnResponseCompleted();
+}
+
+void AppCacheUpdateJob::URLFetcher::OnResponseStarted(
+ net::URLRequest *request) {
+ DCHECK(request == request_);
+ int response_code = -1;
+ if (request->status().is_success()) {
+ response_code = request->GetResponseCode();
+ job_->MadeProgress();
+ }
+ if ((response_code / 100) == 2) {
+
+ // See http://code.google.com/p/chromium/issues/detail?id=69594
+ // We willfully violate the HTML5 spec at this point in order
+ // to support the appcaching of cross-origin HTTPS resources.
+ // We've opted for a milder constraint and allow caching unless
+ // the resource has a "no-store" header. A spec change has been
+ // requested on the whatwg list.
+ // TODO(michaeln): Consider doing this for cross-origin HTTP resources too.
+ if (url_.SchemeIsSecure() &&
+ url_.GetOrigin() != job_->manifest_url_.GetOrigin()) {
+ if (request->response_headers()->
+ HasHeaderValue("cache-control", "no-store")) {
+ DCHECK_EQ(-1, redirect_response_code_);
+ request->Cancel();
+ result_ = SERVER_ERROR; // Not the best match?
+ OnResponseCompleted();
+ return;
+ }
+ }
+
+ // Write response info to storage for URL fetches. Wait for async write
+ // completion before reading any response data.
+ if (fetch_type_ == URL_FETCH || fetch_type_ == MASTER_ENTRY_FETCH) {
+ response_writer_.reset(job_->CreateResponseWriter());
+ scoped_refptr<HttpResponseInfoIOBuffer> io_buffer(
+ new HttpResponseInfoIOBuffer(
+ new net::HttpResponseInfo(request->response_info())));
+ response_writer_->WriteInfo(
+ io_buffer.get(),
+ base::Bind(&URLFetcher::OnWriteComplete, base::Unretained(this)));
+ } else {
+ ReadResponseData();
+ }
+ } else {
+ if (response_code > 0)
+ result_ = SERVER_ERROR;
+ else
+ result_ = NETWORK_ERROR;
+ OnResponseCompleted();
+ }
+}
+
+void AppCacheUpdateJob::URLFetcher::OnReadCompleted(
+ net::URLRequest* request, int bytes_read) {
+ DCHECK(request_ == request);
+ bool data_consumed = true;
+ if (request->status().is_success() && bytes_read > 0) {
+ job_->MadeProgress();
+ data_consumed = ConsumeResponseData(bytes_read);
+ if (data_consumed) {
+ bytes_read = 0;
+ while (request->Read(buffer_.get(), kBufferSize, &bytes_read)) {
+ if (bytes_read > 0) {
+ data_consumed = ConsumeResponseData(bytes_read);
+ if (!data_consumed)
+ break; // wait for async data processing, then read more
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ if (data_consumed && !request->status().is_io_pending()) {
+ DCHECK_EQ(UPDATE_OK, result_);
+ OnResponseCompleted();
+ }
+}
+
+void AppCacheUpdateJob::URLFetcher::AddConditionalHeaders(
+ const net::HttpResponseHeaders* headers) {
+ DCHECK(request_.get() && headers);
+ net::HttpRequestHeaders extra_headers;
+
+ // Add If-Modified-Since header if response info has Last-Modified header.
+ const std::string last_modified = "Last-Modified";
+ std::string last_modified_value;
+ headers->EnumerateHeader(NULL, last_modified, &last_modified_value);
+ if (!last_modified_value.empty()) {
+ extra_headers.SetHeader(net::HttpRequestHeaders::kIfModifiedSince,
+ last_modified_value);
+ }
+
+ // Add If-None-Match header if response info has ETag header.
+ const std::string etag = "ETag";
+ std::string etag_value;
+ headers->EnumerateHeader(NULL, etag, &etag_value);
+ if (!etag_value.empty()) {
+ extra_headers.SetHeader(net::HttpRequestHeaders::kIfNoneMatch,
+ etag_value);
+ }
+ if (!extra_headers.IsEmpty())
+ request_->SetExtraRequestHeaders(extra_headers);
+}
+
+void AppCacheUpdateJob::URLFetcher::OnWriteComplete(int result) {
+ if (result < 0) {
+ request_->Cancel();
+ result_ = DISKCACHE_ERROR;
+ OnResponseCompleted();
+ return;
+ }
+ ReadResponseData();
+}
+
+void AppCacheUpdateJob::URLFetcher::ReadResponseData() {
+ InternalUpdateState state = job_->internal_state_;
+ if (state == CACHE_FAILURE || state == CANCELLED || state == COMPLETED)
+ return;
+ int bytes_read = 0;
+ request_->Read(buffer_.get(), kBufferSize, &bytes_read);
+ OnReadCompleted(request_.get(), bytes_read);
+}
+
+// Returns false if response data is processed asynchronously, in which
+// case ReadResponseData will be invoked when it is safe to continue
+// reading more response data from the request.
+bool AppCacheUpdateJob::URLFetcher::ConsumeResponseData(int bytes_read) {
+ DCHECK_GT(bytes_read, 0);
+ switch (fetch_type_) {
+ case MANIFEST_FETCH:
+ case MANIFEST_REFETCH:
+ manifest_data_.append(buffer_->data(), bytes_read);
+ break;
+ case URL_FETCH:
+ case MASTER_ENTRY_FETCH:
+ DCHECK(response_writer_.get());
+ response_writer_->WriteData(
+ buffer_.get(),
+ bytes_read,
+ base::Bind(&URLFetcher::OnWriteComplete, base::Unretained(this)));
+ return false; // wait for async write completion to continue reading
+ default:
+ NOTREACHED();
+ }
+ return true;
+}
+
+void AppCacheUpdateJob::URLFetcher::OnResponseCompleted() {
+ if (request_->status().is_success())
+ job_->MadeProgress();
+
+ // Retry for 503s where retry-after is 0.
+ if (request_->status().is_success() &&
+ request_->GetResponseCode() == 503 &&
+ MaybeRetryRequest()) {
+ return;
+ }
+
+ switch (fetch_type_) {
+ case MANIFEST_FETCH:
+ job_->HandleManifestFetchCompleted(this);
+ break;
+ case URL_FETCH:
+ job_->HandleUrlFetchCompleted(this);
+ break;
+ case MASTER_ENTRY_FETCH:
+ job_->HandleMasterEntryFetchCompleted(this);
+ break;
+ case MANIFEST_REFETCH:
+ job_->HandleManifestRefetchCompleted(this);
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ delete this;
+}
+
+bool AppCacheUpdateJob::URLFetcher::MaybeRetryRequest() {
+ if (retry_503_attempts_ >= kMax503Retries ||
+ !request_->response_headers()->HasHeaderValue("retry-after", "0")) {
+ return false;
+ }
+ ++retry_503_attempts_;
+ result_ = UPDATE_OK;
+ request_ = job_->service_->request_context()->CreateRequest(
+ url_, net::DEFAULT_PRIORITY, this, NULL);
+ Start();
+ return true;
+}
+
+AppCacheUpdateJob::AppCacheUpdateJob(AppCacheServiceImpl* service,
+ AppCacheGroup* group)
+ : service_(service),
+ manifest_url_(group->manifest_url()),
+ group_(group),
+ update_type_(UNKNOWN_TYPE),
+ internal_state_(FETCH_MANIFEST),
+ master_entries_completed_(0),
+ url_fetches_completed_(0),
+ manifest_fetcher_(NULL),
+ manifest_has_valid_mime_type_(false),
+ stored_state_(UNSTORED),
+ storage_(service->storage()) {
+ service_->AddObserver(this);
+}
+
+AppCacheUpdateJob::~AppCacheUpdateJob() {
+ if (service_)
+ service_->RemoveObserver(this);
+ if (internal_state_ != COMPLETED)
+ Cancel();
+
+ DCHECK(!manifest_fetcher_);
+ DCHECK(pending_url_fetches_.empty());
+ DCHECK(!inprogress_cache_.get());
+ DCHECK(pending_master_entries_.empty());
+ DCHECK(master_entry_fetches_.empty());
+
+ if (group_)
+ group_->SetUpdateAppCacheStatus(AppCacheGroup::IDLE);
+}
+
+void AppCacheUpdateJob::StartUpdate(AppCacheHost* host,
+ const GURL& new_master_resource) {
+ DCHECK(group_->update_job() == this);
+ DCHECK(!group_->is_obsolete());
+
+ bool is_new_pending_master_entry = false;
+ if (!new_master_resource.is_empty()) {
+ DCHECK(new_master_resource == host->pending_master_entry_url());
+ DCHECK(!new_master_resource.has_ref());
+ DCHECK(new_master_resource.GetOrigin() == manifest_url_.GetOrigin());
+
+ // Cannot add more to this update if already terminating.
+ if (IsTerminating()) {
+ group_->QueueUpdate(host, new_master_resource);
+ return;
+ }
+
+ std::pair<PendingMasters::iterator, bool> ret =
+ pending_master_entries_.insert(
+ PendingMasters::value_type(new_master_resource, PendingHosts()));
+ is_new_pending_master_entry = ret.second;
+ ret.first->second.push_back(host);
+ host->AddObserver(this);
+ }
+
+ // Notify host (if any) if already checking or downloading.
+ AppCacheGroup::UpdateAppCacheStatus update_status = group_->update_status();
+ if (update_status == AppCacheGroup::CHECKING ||
+ update_status == AppCacheGroup::DOWNLOADING) {
+ if (host) {
+ NotifySingleHost(host, APPCACHE_CHECKING_EVENT);
+ if (update_status == AppCacheGroup::DOWNLOADING)
+ NotifySingleHost(host, APPCACHE_DOWNLOADING_EVENT);
+
+ // Add to fetch list or an existing entry if already fetched.
+ if (!new_master_resource.is_empty()) {
+ AddMasterEntryToFetchList(host, new_master_resource,
+ is_new_pending_master_entry);
+ }
+ }
+ return;
+ }
+
+ // Begin update process for the group.
+ MadeProgress();
+ group_->SetUpdateAppCacheStatus(AppCacheGroup::CHECKING);
+ if (group_->HasCache()) {
+ update_type_ = UPGRADE_ATTEMPT;
+ NotifyAllAssociatedHosts(APPCACHE_CHECKING_EVENT);
+ } else {
+ update_type_ = CACHE_ATTEMPT;
+ DCHECK(host);
+ NotifySingleHost(host, APPCACHE_CHECKING_EVENT);
+ }
+
+ if (!new_master_resource.is_empty()) {
+ AddMasterEntryToFetchList(host, new_master_resource,
+ is_new_pending_master_entry);
+ }
+
+ FetchManifest(true);
+}
+
+AppCacheResponseWriter* AppCacheUpdateJob::CreateResponseWriter() {
+ AppCacheResponseWriter* writer =
+ storage_->CreateResponseWriter(manifest_url_,
+ group_->group_id());
+ stored_response_ids_.push_back(writer->response_id());
+ return writer;
+}
+
+void AppCacheUpdateJob::HandleCacheFailure(
+ const AppCacheErrorDetails& error_details,
+ ResultType result,
+ const GURL& failed_resource_url) {
+ // 6.9.4 cache failure steps 2-8.
+ DCHECK(internal_state_ != CACHE_FAILURE);
+ DCHECK(!error_details.message.empty());
+ DCHECK(result != UPDATE_OK);
+ internal_state_ = CACHE_FAILURE;
+ LogHistogramStats(result, failed_resource_url);
+ CancelAllUrlFetches();
+ CancelAllMasterEntryFetches(error_details);
+ NotifyAllError(error_details);
+ DiscardInprogressCache();
+ internal_state_ = COMPLETED;
+ DeleteSoon(); // To unwind the stack prior to deletion.
+}
+
+void AppCacheUpdateJob::FetchManifest(bool is_first_fetch) {
+ DCHECK(!manifest_fetcher_);
+ manifest_fetcher_ = new URLFetcher(
+ manifest_url_,
+ is_first_fetch ? URLFetcher::MANIFEST_FETCH :
+ URLFetcher::MANIFEST_REFETCH,
+ this);
+
+ // Add any necessary Http headers before sending fetch request.
+ if (is_first_fetch) {
+ AppCacheEntry* entry = (update_type_ == UPGRADE_ATTEMPT) ?
+ group_->newest_complete_cache()->GetEntry(manifest_url_) : NULL;
+ if (entry) {
+ // Asynchronously load response info for manifest from newest cache.
+ storage_->LoadResponseInfo(manifest_url_, group_->group_id(),
+ entry->response_id(), this);
+ } else {
+ manifest_fetcher_->Start();
+ }
+ } else {
+ DCHECK(internal_state_ == REFETCH_MANIFEST);
+ DCHECK(manifest_response_info_.get());
+ manifest_fetcher_->set_existing_response_headers(
+ manifest_response_info_->headers.get());
+ manifest_fetcher_->Start();
+ }
+}
+
+
+void AppCacheUpdateJob::HandleManifestFetchCompleted(
+ URLFetcher* fetcher) {
+ DCHECK_EQ(internal_state_, FETCH_MANIFEST);
+ DCHECK_EQ(manifest_fetcher_, fetcher);
+ manifest_fetcher_ = NULL;
+
+ net::URLRequest* request = fetcher->request();
+ int response_code = -1;
+ bool is_valid_response_code = false;
+ if (request->status().is_success()) {
+ response_code = request->GetResponseCode();
+ is_valid_response_code = (response_code / 100 == 2);
+
+ std::string mime_type;
+ request->GetMimeType(&mime_type);
+ manifest_has_valid_mime_type_ = (mime_type == "text/cache-manifest");
+ }
+
+ if (is_valid_response_code) {
+ manifest_data_ = fetcher->manifest_data();
+ manifest_response_info_.reset(
+ new net::HttpResponseInfo(request->response_info()));
+ if (update_type_ == UPGRADE_ATTEMPT)
+ CheckIfManifestChanged(); // continues asynchronously
+ else
+ ContinueHandleManifestFetchCompleted(true);
+ } else if (response_code == 304 && update_type_ == UPGRADE_ATTEMPT) {
+ ContinueHandleManifestFetchCompleted(false);
+ } else if ((response_code == 404 || response_code == 410) &&
+ update_type_ == UPGRADE_ATTEMPT) {
+ storage_->MakeGroupObsolete(group_, this, response_code); // async
+ } else {
+ const char* kFormatString = "Manifest fetch failed (%d) %s";
+ std::string message = FormatUrlErrorMessage(
+ kFormatString, manifest_url_, fetcher->result(), response_code);
+ HandleCacheFailure(AppCacheErrorDetails(message,
+ APPCACHE_MANIFEST_ERROR,
+ manifest_url_,
+ response_code,
+ false /*is_cross_origin*/),
+ fetcher->result(),
+ GURL());
+ }
+}
+
+void AppCacheUpdateJob::OnGroupMadeObsolete(AppCacheGroup* group,
+ bool success,
+ int response_code) {
+ DCHECK(master_entry_fetches_.empty());
+ CancelAllMasterEntryFetches(AppCacheErrorDetails(
+ "The cache has been made obsolete, "
+ "the manifest file returned 404 or 410",
+ APPCACHE_MANIFEST_ERROR,
+ GURL(),
+ response_code,
+ false /*is_cross_origin*/));
+ if (success) {
+ DCHECK(group->is_obsolete());
+ NotifyAllAssociatedHosts(APPCACHE_OBSOLETE_EVENT);
+ internal_state_ = COMPLETED;
+ MaybeCompleteUpdate();
+ } else {
+ // Treat failure to mark group obsolete as a cache failure.
+ HandleCacheFailure(AppCacheErrorDetails(
+ "Failed to mark the cache as obsolete",
+ APPCACHE_UNKNOWN_ERROR,
+ GURL(),
+ 0,
+ false /*is_cross_origin*/),
+ DB_ERROR,
+ GURL());
+ }
+}
+
+void AppCacheUpdateJob::ContinueHandleManifestFetchCompleted(bool changed) {
+ DCHECK(internal_state_ == FETCH_MANIFEST);
+
+ if (!changed) {
+ DCHECK(update_type_ == UPGRADE_ATTEMPT);
+ internal_state_ = NO_UPDATE;
+
+ // Wait for pending master entries to download.
+ FetchMasterEntries();
+ MaybeCompleteUpdate(); // if not done, run async 6.9.4 step 7 substeps
+ return;
+ }
+
+ Manifest manifest;
+ if (!ParseManifest(manifest_url_, manifest_data_.data(),
+ manifest_data_.length(),
+ manifest_has_valid_mime_type_ ?
+ PARSE_MANIFEST_ALLOWING_INTERCEPTS :
+ PARSE_MANIFEST_PER_STANDARD,
+ manifest)) {
+ const char* kFormatString = "Failed to parse manifest %s";
+ const std::string message = base::StringPrintf(kFormatString,
+ manifest_url_.spec().c_str());
+ HandleCacheFailure(
+ AppCacheErrorDetails(
+ message, APPCACHE_SIGNATURE_ERROR, GURL(), 0,
+ false /*is_cross_origin*/),
+ MANIFEST_ERROR,
+ GURL());
+ VLOG(1) << message;
+ return;
+ }
+
+ // Proceed with update process. Section 6.9.4 steps 8-20.
+ internal_state_ = DOWNLOADING;
+ inprogress_cache_ = new AppCache(storage_, storage_->NewCacheId());
+ BuildUrlFileList(manifest);
+ inprogress_cache_->InitializeWithManifest(&manifest);
+
+ // Associate all pending master hosts with the newly created cache.
+ for (PendingMasters::iterator it = pending_master_entries_.begin();
+ it != pending_master_entries_.end(); ++it) {
+ PendingHosts& hosts = it->second;
+ for (PendingHosts::iterator host_it = hosts.begin();
+ host_it != hosts.end(); ++host_it) {
+ (*host_it)
+ ->AssociateIncompleteCache(inprogress_cache_.get(), manifest_url_);
+ }
+ }
+
+ if (manifest.did_ignore_intercept_namespaces) {
+ // Must be done after associating all pending master hosts.
+ std::string message(
+ "Ignoring the INTERCEPT section of the application cache manifest "
+ "because the content type is not text/cache-manifest");
+ LogConsoleMessageToAll(message);
+ }
+
+ group_->SetUpdateAppCacheStatus(AppCacheGroup::DOWNLOADING);
+ NotifyAllAssociatedHosts(APPCACHE_DOWNLOADING_EVENT);
+ FetchUrls();
+ FetchMasterEntries();
+ MaybeCompleteUpdate(); // if not done, continues when async fetches complete
+}
+
+void AppCacheUpdateJob::HandleUrlFetchCompleted(URLFetcher* fetcher) {
+ DCHECK(internal_state_ == DOWNLOADING);
+
+ net::URLRequest* request = fetcher->request();
+ const GURL& url = request->original_url();
+ pending_url_fetches_.erase(url);
+ NotifyAllProgress(url);
+ ++url_fetches_completed_;
+
+ int response_code = request->status().is_success()
+ ? request->GetResponseCode()
+ : fetcher->redirect_response_code();
+
+ AppCacheEntry& entry = url_file_list_.find(url)->second;
+
+ if (response_code / 100 == 2) {
+ // Associate storage with the new entry.
+ DCHECK(fetcher->response_writer());
+ entry.set_response_id(fetcher->response_writer()->response_id());
+ entry.set_response_size(fetcher->response_writer()->amount_written());
+ if (!inprogress_cache_->AddOrModifyEntry(url, entry))
+ duplicate_response_ids_.push_back(entry.response_id());
+
+ // TODO(michaeln): Check for <html manifest=xxx>
+ // See http://code.google.com/p/chromium/issues/detail?id=97930
+ // if (entry.IsMaster() && !(entry.IsExplicit() || fallback || intercept))
+ // if (!manifestAttribute) skip it
+
+ // Foreign entries will be detected during cache selection.
+ // Note: 6.9.4, step 17.9 possible optimization: if resource is HTML or XML
+ // file whose root element is an html element with a manifest attribute
+ // whose value doesn't match the manifest url of the application cache
+ // being processed, mark the entry as being foreign.
+ } else {
+ VLOG(1) << "Request status: " << request->status().status()
+ << " error: " << request->status().error()
+ << " response code: " << response_code;
+ if (entry.IsExplicit() || entry.IsFallback() || entry.IsIntercept()) {
+ if (response_code == 304 && fetcher->existing_entry().has_response_id()) {
+ // Keep the existing response.
+ entry.set_response_id(fetcher->existing_entry().response_id());
+ entry.set_response_size(fetcher->existing_entry().response_size());
+ inprogress_cache_->AddOrModifyEntry(url, entry);
+ } else {
+ const char* kFormatString = "Resource fetch failed (%d) %s";
+ std::string message = FormatUrlErrorMessage(
+ kFormatString, url, fetcher->result(), response_code);
+ ResultType result = fetcher->result();
+ bool is_cross_origin = url.GetOrigin() != manifest_url_.GetOrigin();
+ switch (result) {
+ case DISKCACHE_ERROR:
+ HandleCacheFailure(
+ AppCacheErrorDetails(
+ message, APPCACHE_UNKNOWN_ERROR, GURL(), 0,
+ is_cross_origin),
+ result,
+ url);
+ break;
+ case NETWORK_ERROR:
+ HandleCacheFailure(
+ AppCacheErrorDetails(message, APPCACHE_RESOURCE_ERROR, url, 0,
+ is_cross_origin),
+ result,
+ url);
+ break;
+ default:
+ HandleCacheFailure(AppCacheErrorDetails(message,
+ APPCACHE_RESOURCE_ERROR,
+ url,
+ response_code,
+ is_cross_origin),
+ result,
+ url);
+ break;
+ }
+ return;
+ }
+ } else if (response_code == 404 || response_code == 410) {
+ // Entry is skipped. They are dropped from the cache.
+ } else if (update_type_ == UPGRADE_ATTEMPT &&
+ fetcher->existing_entry().has_response_id()) {
+ // Keep the existing response.
+ // TODO(michaeln): Not sure this is a good idea. This is spec compliant
+ // but the old resource may or may not be compatible with the new contents
+ // of the cache. Impossible to know one way or the other.
+ entry.set_response_id(fetcher->existing_entry().response_id());
+ entry.set_response_size(fetcher->existing_entry().response_size());
+ inprogress_cache_->AddOrModifyEntry(url, entry);
+ }
+ }
+
+ // Fetch another URL now that one request has completed.
+ DCHECK(internal_state_ != CACHE_FAILURE);
+ FetchUrls();
+ MaybeCompleteUpdate();
+}
+
+void AppCacheUpdateJob::HandleMasterEntryFetchCompleted(
+ URLFetcher* fetcher) {
+ DCHECK(internal_state_ == NO_UPDATE || internal_state_ == DOWNLOADING);
+
+ // TODO(jennb): Handle downloads completing during cache failure when update
+ // no longer fetches master entries directly. For now, we cancel all pending
+ // master entry fetches when entering cache failure state so this will never
+ // be called in CACHE_FAILURE state.
+
+ net::URLRequest* request = fetcher->request();
+ const GURL& url = request->original_url();
+ master_entry_fetches_.erase(url);
+ ++master_entries_completed_;
+
+ int response_code = request->status().is_success()
+ ? request->GetResponseCode() : -1;
+
+ PendingMasters::iterator found = pending_master_entries_.find(url);
+ DCHECK(found != pending_master_entries_.end());
+ PendingHosts& hosts = found->second;
+
+ // Section 6.9.4. No update case: step 7.3, else step 22.
+ if (response_code / 100 == 2) {
+ // Add fetched master entry to the appropriate cache.
+ AppCache* cache = inprogress_cache_.get() ? inprogress_cache_.get()
+ : group_->newest_complete_cache();
+ DCHECK(fetcher->response_writer());
+ AppCacheEntry master_entry(AppCacheEntry::MASTER,
+ fetcher->response_writer()->response_id(),
+ fetcher->response_writer()->amount_written());
+ if (cache->AddOrModifyEntry(url, master_entry))
+ added_master_entries_.push_back(url);
+ else
+ duplicate_response_ids_.push_back(master_entry.response_id());
+
+ // In no-update case, associate host with the newest cache.
+ if (!inprogress_cache_.get()) {
+ // TODO(michaeln): defer until the updated cache has been stored
+ DCHECK(cache == group_->newest_complete_cache());
+ for (PendingHosts::iterator host_it = hosts.begin();
+ host_it != hosts.end(); ++host_it) {
+ (*host_it)->AssociateCompleteCache(cache);
+ }
+ }
+ } else {
+ HostNotifier host_notifier;
+ for (PendingHosts::iterator host_it = hosts.begin();
+ host_it != hosts.end(); ++host_it) {
+ AppCacheHost* host = *host_it;
+ host_notifier.AddHost(host);
+
+ // In downloading case, disassociate host from inprogress cache.
+ if (inprogress_cache_.get())
+ host->AssociateNoCache(GURL());
+
+ host->RemoveObserver(this);
+ }
+ hosts.clear();
+
+ const char* kFormatString = "Manifest fetch failed (%d) %s";
+ std::string message = FormatUrlErrorMessage(
+ kFormatString, request->url(), fetcher->result(), response_code);
+ host_notifier.SendErrorNotifications(
+ AppCacheErrorDetails(message,
+ APPCACHE_MANIFEST_ERROR,
+ request->url(),
+ response_code,
+ false /*is_cross_origin*/));
+
+ // In downloading case, update result is different if all master entries
+ // failed vs. only some failing.
+ if (inprogress_cache_.get()) {
+ // Only count successful downloads to know if all master entries failed.
+ pending_master_entries_.erase(found);
+ --master_entries_completed_;
+
+ // Section 6.9.4, step 22.3.
+ if (update_type_ == CACHE_ATTEMPT && pending_master_entries_.empty()) {
+ HandleCacheFailure(AppCacheErrorDetails(message,
+ APPCACHE_MANIFEST_ERROR,
+ request->url(),
+ response_code,
+ false /*is_cross_origin*/),
+ fetcher->result(),
+ GURL());
+ return;
+ }
+ }
+ }
+
+ DCHECK(internal_state_ != CACHE_FAILURE);
+ FetchMasterEntries();
+ MaybeCompleteUpdate();
+}
+
+void AppCacheUpdateJob::HandleManifestRefetchCompleted(
+ URLFetcher* fetcher) {
+ DCHECK(internal_state_ == REFETCH_MANIFEST);
+ DCHECK(manifest_fetcher_ == fetcher);
+ manifest_fetcher_ = NULL;
+
+ net::URLRequest* request = fetcher->request();
+ int response_code = request->status().is_success()
+ ? request->GetResponseCode() : -1;
+ if (response_code == 304 || manifest_data_ == fetcher->manifest_data()) {
+ // Only need to store response in storage if manifest is not already
+ // an entry in the cache.
+ AppCacheEntry* entry = inprogress_cache_->GetEntry(manifest_url_);
+ if (entry) {
+ entry->add_types(AppCacheEntry::MANIFEST);
+ StoreGroupAndCache();
+ } else {
+ manifest_response_writer_.reset(CreateResponseWriter());
+ scoped_refptr<HttpResponseInfoIOBuffer> io_buffer(
+ new HttpResponseInfoIOBuffer(manifest_response_info_.release()));
+ manifest_response_writer_->WriteInfo(
+ io_buffer.get(),
+ base::Bind(&AppCacheUpdateJob::OnManifestInfoWriteComplete,
+ base::Unretained(this)));
+ }
+ } else {
+ VLOG(1) << "Request status: " << request->status().status()
+ << " error: " << request->status().error()
+ << " response code: " << response_code;
+ ScheduleUpdateRetry(kRerunDelayMs);
+ if (response_code == 200) {
+ HandleCacheFailure(AppCacheErrorDetails("Manifest changed during update",
+ APPCACHE_CHANGED_ERROR,
+ GURL(),
+ 0,
+ false /*is_cross_origin*/),
+ MANIFEST_ERROR,
+ GURL());
+ } else {
+ const char* kFormatString = "Manifest re-fetch failed (%d) %s";
+ std::string message = FormatUrlErrorMessage(
+ kFormatString, manifest_url_, fetcher->result(), response_code);
+ HandleCacheFailure(AppCacheErrorDetails(message,
+ APPCACHE_MANIFEST_ERROR,
+ GURL(),
+ response_code,
+ false /*is_cross_origin*/),
+ fetcher->result(),
+ GURL());
+ }
+ }
+}
+
+void AppCacheUpdateJob::OnManifestInfoWriteComplete(int result) {
+ if (result > 0) {
+ scoped_refptr<net::StringIOBuffer> io_buffer(
+ new net::StringIOBuffer(manifest_data_));
+ manifest_response_writer_->WriteData(
+ io_buffer.get(),
+ manifest_data_.length(),
+ base::Bind(&AppCacheUpdateJob::OnManifestDataWriteComplete,
+ base::Unretained(this)));
+ } else {
+ HandleCacheFailure(
+ AppCacheErrorDetails("Failed to write the manifest headers to storage",
+ APPCACHE_UNKNOWN_ERROR,
+ GURL(),
+ 0,
+ false /*is_cross_origin*/),
+ DISKCACHE_ERROR,
+ GURL());
+ }
+}
+
+void AppCacheUpdateJob::OnManifestDataWriteComplete(int result) {
+ if (result > 0) {
+ AppCacheEntry entry(AppCacheEntry::MANIFEST,
+ manifest_response_writer_->response_id(),
+ manifest_response_writer_->amount_written());
+ if (!inprogress_cache_->AddOrModifyEntry(manifest_url_, entry))
+ duplicate_response_ids_.push_back(entry.response_id());
+ StoreGroupAndCache();
+ } else {
+ HandleCacheFailure(
+ AppCacheErrorDetails("Failed to write the manifest data to storage",
+ APPCACHE_UNKNOWN_ERROR,
+ GURL(),
+ 0,
+ false /*is_cross_origin*/),
+ DISKCACHE_ERROR,
+ GURL());
+ }
+}
+
+void AppCacheUpdateJob::StoreGroupAndCache() {
+ DCHECK(stored_state_ == UNSTORED);
+ stored_state_ = STORING;
+ scoped_refptr<AppCache> newest_cache;
+ if (inprogress_cache_.get())
+ newest_cache.swap(inprogress_cache_);
+ else
+ newest_cache = group_->newest_complete_cache();
+ newest_cache->set_update_time(base::Time::Now());
+
+ // TODO(michaeln): dcheck is fishing for clues to crbug/95101
+ DCHECK_EQ(manifest_url_, group_->manifest_url());
+ storage_->StoreGroupAndNewestCache(group_, newest_cache.get(), this);
+}
+
+void AppCacheUpdateJob::OnGroupAndNewestCacheStored(AppCacheGroup* group,
+ AppCache* newest_cache,
+ bool success,
+ bool would_exceed_quota) {
+ DCHECK(stored_state_ == STORING);
+ if (success) {
+ stored_state_ = STORED;
+ MaybeCompleteUpdate(); // will definitely complete
+ } else {
+ stored_state_ = UNSTORED;
+
+ // Restore inprogress_cache_ to get the proper events delivered
+ // and the proper cleanup to occur.
+ if (newest_cache != group->newest_complete_cache())
+ inprogress_cache_ = newest_cache;
+
+ ResultType result = DB_ERROR;
+ AppCacheErrorReason reason = APPCACHE_UNKNOWN_ERROR;
+ std::string message("Failed to commit new cache to storage");
+ if (would_exceed_quota) {
+ message.append(", would exceed quota");
+ result = QUOTA_ERROR;
+ reason = APPCACHE_QUOTA_ERROR;
+ }
+ HandleCacheFailure(
+ AppCacheErrorDetails(message, reason, GURL(), 0,
+ false /*is_cross_origin*/),
+ result,
+ GURL());
+ }
+}
+
+void AppCacheUpdateJob::NotifySingleHost(AppCacheHost* host,
+ AppCacheEventID event_id) {
+ std::vector<int> ids(1, host->host_id());
+ host->frontend()->OnEventRaised(ids, event_id);
+}
+
+void AppCacheUpdateJob::NotifyAllAssociatedHosts(AppCacheEventID event_id) {
+ HostNotifier host_notifier;
+ AddAllAssociatedHostsToNotifier(&host_notifier);
+ host_notifier.SendNotifications(event_id);
+}
+
+void AppCacheUpdateJob::NotifyAllProgress(const GURL& url) {
+ HostNotifier host_notifier;
+ AddAllAssociatedHostsToNotifier(&host_notifier);
+ host_notifier.SendProgressNotifications(
+ url, url_file_list_.size(), url_fetches_completed_);
+}
+
+void AppCacheUpdateJob::NotifyAllFinalProgress() {
+ DCHECK(url_file_list_.size() == url_fetches_completed_);
+ NotifyAllProgress(GURL());
+}
+
+void AppCacheUpdateJob::NotifyAllError(const AppCacheErrorDetails& details) {
+ HostNotifier host_notifier;
+ AddAllAssociatedHostsToNotifier(&host_notifier);
+ host_notifier.SendErrorNotifications(details);
+}
+
+void AppCacheUpdateJob::LogConsoleMessageToAll(const std::string& message) {
+ HostNotifier host_notifier;
+ AddAllAssociatedHostsToNotifier(&host_notifier);
+ host_notifier.SendLogMessage(message);
+}
+
+void AppCacheUpdateJob::AddAllAssociatedHostsToNotifier(
+ HostNotifier* host_notifier) {
+ // Collect hosts so we only send one notification per frontend.
+ // A host can only be associated with a single cache so no need to worry
+ // about duplicate hosts being added to the notifier.
+ if (inprogress_cache_.get()) {
+ DCHECK(internal_state_ == DOWNLOADING || internal_state_ == CACHE_FAILURE);
+ host_notifier->AddHosts(inprogress_cache_->associated_hosts());
+ }
+
+ AppCacheGroup::Caches old_caches = group_->old_caches();
+ for (AppCacheGroup::Caches::const_iterator it = old_caches.begin();
+ it != old_caches.end(); ++it) {
+ host_notifier->AddHosts((*it)->associated_hosts());
+ }
+
+ AppCache* newest_cache = group_->newest_complete_cache();
+ if (newest_cache)
+ host_notifier->AddHosts(newest_cache->associated_hosts());
+}
+
+void AppCacheUpdateJob::OnDestructionImminent(AppCacheHost* host) {
+ // The host is about to be deleted; remove from our collection.
+ PendingMasters::iterator found =
+ pending_master_entries_.find(host->pending_master_entry_url());
+ DCHECK(found != pending_master_entries_.end());
+ PendingHosts& hosts = found->second;
+ PendingHosts::iterator it = std::find(hosts.begin(), hosts.end(), host);
+ DCHECK(it != hosts.end());
+ hosts.erase(it);
+}
+
+void AppCacheUpdateJob::OnServiceReinitialized(
+ AppCacheStorageReference* old_storage_ref) {
+ // We continue to use the disabled instance, but arrange for its
+ // deletion when its no longer needed.
+ if (old_storage_ref->storage() == storage_)
+ disabled_storage_reference_ = old_storage_ref;
+}
+
+void AppCacheUpdateJob::CheckIfManifestChanged() {
+ DCHECK(update_type_ == UPGRADE_ATTEMPT);
+ AppCacheEntry* entry = NULL;
+ if (group_->newest_complete_cache())
+ entry = group_->newest_complete_cache()->GetEntry(manifest_url_);
+ if (!entry) {
+ // TODO(michaeln): This is just a bandaid to avoid a crash.
+ // http://code.google.com/p/chromium/issues/detail?id=95101
+ if (service_->storage() == storage_) {
+ // Use a local variable because service_ is reset in HandleCacheFailure.
+ AppCacheServiceImpl* service = service_;
+ HandleCacheFailure(
+ AppCacheErrorDetails("Manifest entry not found in existing cache",
+ APPCACHE_UNKNOWN_ERROR,
+ GURL(),
+ 0,
+ false /*is_cross_origin*/),
+ DB_ERROR,
+ GURL());
+ AppCacheHistograms::AddMissingManifestEntrySample();
+ service->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
+ }
+ return;
+ }
+
+ // Load manifest data from storage to compare against fetched manifest.
+ manifest_response_reader_.reset(
+ storage_->CreateResponseReader(manifest_url_,
+ group_->group_id(),
+ entry->response_id()));
+ read_manifest_buffer_ = new net::IOBuffer(kBufferSize);
+ manifest_response_reader_->ReadData(
+ read_manifest_buffer_.get(),
+ kBufferSize,
+ base::Bind(&AppCacheUpdateJob::OnManifestDataReadComplete,
+ base::Unretained(this))); // async read
+}
+
+void AppCacheUpdateJob::OnManifestDataReadComplete(int result) {
+ if (result > 0) {
+ loaded_manifest_data_.append(read_manifest_buffer_->data(), result);
+ manifest_response_reader_->ReadData(
+ read_manifest_buffer_.get(),
+ kBufferSize,
+ base::Bind(&AppCacheUpdateJob::OnManifestDataReadComplete,
+ base::Unretained(this))); // read more
+ } else {
+ read_manifest_buffer_ = NULL;
+ manifest_response_reader_.reset();
+ ContinueHandleManifestFetchCompleted(
+ result < 0 || manifest_data_ != loaded_manifest_data_);
+ }
+}
+
+void AppCacheUpdateJob::BuildUrlFileList(const Manifest& manifest) {
+ for (base::hash_set<std::string>::const_iterator it =
+ manifest.explicit_urls.begin();
+ it != manifest.explicit_urls.end(); ++it) {
+ AddUrlToFileList(GURL(*it), AppCacheEntry::EXPLICIT);
+ }
+
+ const std::vector<AppCacheNamespace>& intercepts =
+ manifest.intercept_namespaces;
+ for (std::vector<AppCacheNamespace>::const_iterator it = intercepts.begin();
+ it != intercepts.end(); ++it) {
+ int flags = AppCacheEntry::INTERCEPT;
+ if (it->is_executable)
+ flags |= AppCacheEntry::EXECUTABLE;
+ AddUrlToFileList(it->target_url, flags);
+ }
+
+ const std::vector<AppCacheNamespace>& fallbacks =
+ manifest.fallback_namespaces;
+ for (std::vector<AppCacheNamespace>::const_iterator it = fallbacks.begin();
+ it != fallbacks.end(); ++it) {
+ AddUrlToFileList(it->target_url, AppCacheEntry::FALLBACK);
+ }
+
+ // Add all master entries from newest complete cache.
+ if (update_type_ == UPGRADE_ATTEMPT) {
+ const AppCache::EntryMap& entries =
+ group_->newest_complete_cache()->entries();
+ for (AppCache::EntryMap::const_iterator it = entries.begin();
+ it != entries.end(); ++it) {
+ const AppCacheEntry& entry = it->second;
+ if (entry.IsMaster())
+ AddUrlToFileList(it->first, AppCacheEntry::MASTER);
+ }
+ }
+}
+
+void AppCacheUpdateJob::AddUrlToFileList(const GURL& url, int type) {
+ std::pair<AppCache::EntryMap::iterator, bool> ret = url_file_list_.insert(
+ AppCache::EntryMap::value_type(url, AppCacheEntry(type)));
+
+ if (ret.second)
+ urls_to_fetch_.push_back(UrlToFetch(url, false, NULL));
+ else
+ ret.first->second.add_types(type); // URL already exists. Merge types.
+}
+
+void AppCacheUpdateJob::FetchUrls() {
+ DCHECK(internal_state_ == DOWNLOADING);
+
+ // Fetch each URL in the list according to section 6.9.4 step 17.1-17.3.
+ // Fetch up to the concurrent limit. Other fetches will be triggered as each
+ // each fetch completes.
+ while (pending_url_fetches_.size() < kMaxConcurrentUrlFetches &&
+ !urls_to_fetch_.empty()) {
+ UrlToFetch url_to_fetch = urls_to_fetch_.front();
+ urls_to_fetch_.pop_front();
+
+ AppCache::EntryMap::iterator it = url_file_list_.find(url_to_fetch.url);
+ DCHECK(it != url_file_list_.end());
+ AppCacheEntry& entry = it->second;
+ if (ShouldSkipUrlFetch(entry)) {
+ NotifyAllProgress(url_to_fetch.url);
+ ++url_fetches_completed_;
+ } else if (AlreadyFetchedEntry(url_to_fetch.url, entry.types())) {
+ NotifyAllProgress(url_to_fetch.url);
+ ++url_fetches_completed_; // saved a URL request
+ } else if (!url_to_fetch.storage_checked &&
+ MaybeLoadFromNewestCache(url_to_fetch.url, entry)) {
+ // Continues asynchronously after data is loaded from newest cache.
+ } else {
+ URLFetcher* fetcher = new URLFetcher(
+ url_to_fetch.url, URLFetcher::URL_FETCH, this);
+ if (url_to_fetch.existing_response_info.get()) {
+ DCHECK(group_->newest_complete_cache());
+ AppCacheEntry* existing_entry =
+ group_->newest_complete_cache()->GetEntry(url_to_fetch.url);
+ DCHECK(existing_entry);
+ DCHECK(existing_entry->response_id() ==
+ url_to_fetch.existing_response_info->response_id());
+ fetcher->set_existing_response_headers(
+ url_to_fetch.existing_response_info->http_response_info()->headers
+ .get());
+ fetcher->set_existing_entry(*existing_entry);
+ }
+ fetcher->Start();
+ pending_url_fetches_.insert(
+ PendingUrlFetches::value_type(url_to_fetch.url, fetcher));
+ }
+ }
+}
+
+void AppCacheUpdateJob::CancelAllUrlFetches() {
+ // Cancel any pending URL requests.
+ for (PendingUrlFetches::iterator it = pending_url_fetches_.begin();
+ it != pending_url_fetches_.end(); ++it) {
+ delete it->second;
+ }
+
+ url_fetches_completed_ +=
+ pending_url_fetches_.size() + urls_to_fetch_.size();
+ pending_url_fetches_.clear();
+ urls_to_fetch_.clear();
+}
+
+bool AppCacheUpdateJob::ShouldSkipUrlFetch(const AppCacheEntry& entry) {
+ // 6.6.4 Step 17
+ // If the resource URL being processed was flagged as neither an
+ // "explicit entry" nor or a "fallback entry", then the user agent
+ // may skip this URL.
+ if (entry.IsExplicit() || entry.IsFallback() || entry.IsIntercept())
+ return false;
+
+ // TODO(jennb): decide if entry should be skipped to expire it from cache
+ return false;
+}
+
+bool AppCacheUpdateJob::AlreadyFetchedEntry(const GURL& url,
+ int entry_type) {
+ DCHECK(internal_state_ == DOWNLOADING || internal_state_ == NO_UPDATE);
+ AppCacheEntry* existing =
+ inprogress_cache_.get() ? inprogress_cache_->GetEntry(url)
+ : group_->newest_complete_cache()->GetEntry(url);
+ if (existing) {
+ existing->add_types(entry_type);
+ return true;
+ }
+ return false;
+}
+
+void AppCacheUpdateJob::AddMasterEntryToFetchList(AppCacheHost* host,
+ const GURL& url,
+ bool is_new) {
+ DCHECK(!IsTerminating());
+
+ if (internal_state_ == DOWNLOADING || internal_state_ == NO_UPDATE) {
+ AppCache* cache;
+ if (inprogress_cache_.get()) {
+ // always associate
+ host->AssociateIncompleteCache(inprogress_cache_.get(), manifest_url_);
+ cache = inprogress_cache_.get();
+ } else {
+ cache = group_->newest_complete_cache();
+ }
+
+ // Update existing entry if it has already been fetched.
+ AppCacheEntry* entry = cache->GetEntry(url);
+ if (entry) {
+ entry->add_types(AppCacheEntry::MASTER);
+ if (internal_state_ == NO_UPDATE && !inprogress_cache_.get()) {
+ // only associate if have entry
+ host->AssociateCompleteCache(cache);
+ }
+ if (is_new)
+ ++master_entries_completed_; // pretend fetching completed
+ return;
+ }
+ }
+
+ // Add to fetch list if not already fetching.
+ if (master_entry_fetches_.find(url) == master_entry_fetches_.end()) {
+ master_entries_to_fetch_.insert(url);
+ if (internal_state_ == DOWNLOADING || internal_state_ == NO_UPDATE)
+ FetchMasterEntries();
+ }
+}
+
+void AppCacheUpdateJob::FetchMasterEntries() {
+ DCHECK(internal_state_ == NO_UPDATE || internal_state_ == DOWNLOADING);
+
+ // Fetch each master entry in the list, up to the concurrent limit.
+ // Additional fetches will be triggered as each fetch completes.
+ while (master_entry_fetches_.size() < kMaxConcurrentUrlFetches &&
+ !master_entries_to_fetch_.empty()) {
+ const GURL& url = *master_entries_to_fetch_.begin();
+
+ if (AlreadyFetchedEntry(url, AppCacheEntry::MASTER)) {
+ ++master_entries_completed_; // saved a URL request
+
+ // In no update case, associate hosts to newest cache in group
+ // now that master entry has been "successfully downloaded".
+ if (internal_state_ == NO_UPDATE) {
+ // TODO(michaeln): defer until the updated cache has been stored.
+ DCHECK(!inprogress_cache_.get());
+ AppCache* cache = group_->newest_complete_cache();
+ PendingMasters::iterator found = pending_master_entries_.find(url);
+ DCHECK(found != pending_master_entries_.end());
+ PendingHosts& hosts = found->second;
+ for (PendingHosts::iterator host_it = hosts.begin();
+ host_it != hosts.end(); ++host_it) {
+ (*host_it)->AssociateCompleteCache(cache);
+ }
+ }
+ } else {
+ URLFetcher* fetcher = new URLFetcher(
+ url, URLFetcher::MASTER_ENTRY_FETCH, this);
+ fetcher->Start();
+ master_entry_fetches_.insert(PendingUrlFetches::value_type(url, fetcher));
+ }
+
+ master_entries_to_fetch_.erase(master_entries_to_fetch_.begin());
+ }
+}
+
+void AppCacheUpdateJob::CancelAllMasterEntryFetches(
+ const AppCacheErrorDetails& error_details) {
+ // For now, cancel all in-progress fetches for master entries and pretend
+ // all master entries fetches have completed.
+ // TODO(jennb): Delete this when update no longer fetches master entries
+ // directly.
+
+ // Cancel all in-progress fetches.
+ for (PendingUrlFetches::iterator it = master_entry_fetches_.begin();
+ it != master_entry_fetches_.end(); ++it) {
+ delete it->second;
+ master_entries_to_fetch_.insert(it->first); // back in unfetched list
+ }
+ master_entry_fetches_.clear();
+
+ master_entries_completed_ += master_entries_to_fetch_.size();
+
+ // Cache failure steps, step 2.
+ // Pretend all master entries that have not yet been fetched have completed
+ // downloading. Unassociate hosts from any appcache and send ERROR event.
+ HostNotifier host_notifier;
+ while (!master_entries_to_fetch_.empty()) {
+ const GURL& url = *master_entries_to_fetch_.begin();
+ PendingMasters::iterator found = pending_master_entries_.find(url);
+ DCHECK(found != pending_master_entries_.end());
+ PendingHosts& hosts = found->second;
+ for (PendingHosts::iterator host_it = hosts.begin();
+ host_it != hosts.end(); ++host_it) {
+ AppCacheHost* host = *host_it;
+ host->AssociateNoCache(GURL());
+ host_notifier.AddHost(host);
+ host->RemoveObserver(this);
+ }
+ hosts.clear();
+
+ master_entries_to_fetch_.erase(master_entries_to_fetch_.begin());
+ }
+ host_notifier.SendErrorNotifications(error_details);
+}
+
+bool AppCacheUpdateJob::MaybeLoadFromNewestCache(const GURL& url,
+ AppCacheEntry& entry) {
+ if (update_type_ != UPGRADE_ATTEMPT)
+ return false;
+
+ AppCache* newest = group_->newest_complete_cache();
+ AppCacheEntry* copy_me = newest->GetEntry(url);
+ if (!copy_me || !copy_me->has_response_id())
+ return false;
+
+ // Load HTTP headers for entry from newest cache.
+ loading_responses_.insert(
+ LoadingResponses::value_type(copy_me->response_id(), url));
+ storage_->LoadResponseInfo(manifest_url_, group_->group_id(),
+ copy_me->response_id(),
+ this);
+ // Async: wait for OnResponseInfoLoaded to complete.
+ return true;
+}
+
+void AppCacheUpdateJob::OnResponseInfoLoaded(
+ AppCacheResponseInfo* response_info, int64 response_id) {
+ const net::HttpResponseInfo* http_info = response_info ?
+ response_info->http_response_info() : NULL;
+
+ // Needed response info for a manifest fetch request.
+ if (internal_state_ == FETCH_MANIFEST) {
+ if (http_info)
+ manifest_fetcher_->set_existing_response_headers(
+ http_info->headers.get());
+ manifest_fetcher_->Start();
+ return;
+ }
+
+ LoadingResponses::iterator found = loading_responses_.find(response_id);
+ DCHECK(found != loading_responses_.end());
+ const GURL& url = found->second;
+
+ if (!http_info) {
+ LoadFromNewestCacheFailed(url, NULL); // no response found
+ } else {
+ // Check if response can be re-used according to HTTP caching semantics.
+ // Responses with a "vary" header get treated as expired.
+ const std::string name = "vary";
+ std::string value;
+ void* iter = NULL;
+ if (!http_info->headers.get() ||
+ http_info->headers->RequiresValidation(http_info->request_time,
+ http_info->response_time,
+ base::Time::Now()) ||
+ http_info->headers->EnumerateHeader(&iter, name, &value)) {
+ LoadFromNewestCacheFailed(url, response_info);
+ } else {
+ DCHECK(group_->newest_complete_cache());
+ AppCacheEntry* copy_me = group_->newest_complete_cache()->GetEntry(url);
+ DCHECK(copy_me);
+ DCHECK(copy_me->response_id() == response_id);
+
+ AppCache::EntryMap::iterator it = url_file_list_.find(url);
+ DCHECK(it != url_file_list_.end());
+ AppCacheEntry& entry = it->second;
+ entry.set_response_id(response_id);
+ entry.set_response_size(copy_me->response_size());
+ inprogress_cache_->AddOrModifyEntry(url, entry);
+ NotifyAllProgress(url);
+ ++url_fetches_completed_;
+ }
+ }
+ loading_responses_.erase(found);
+
+ MaybeCompleteUpdate();
+}
+
+void AppCacheUpdateJob::LoadFromNewestCacheFailed(
+ const GURL& url, AppCacheResponseInfo* response_info) {
+ if (internal_state_ == CACHE_FAILURE)
+ return;
+
+ // Re-insert url at front of fetch list. Indicate storage has been checked.
+ urls_to_fetch_.push_front(UrlToFetch(url, true, response_info));
+ FetchUrls();
+}
+
+void AppCacheUpdateJob::MaybeCompleteUpdate() {
+ DCHECK(internal_state_ != CACHE_FAILURE);
+
+ // Must wait for any pending master entries or url fetches to complete.
+ if (master_entries_completed_ != pending_master_entries_.size() ||
+ url_fetches_completed_ != url_file_list_.size()) {
+ DCHECK(internal_state_ != COMPLETED);
+ return;
+ }
+
+ switch (internal_state_) {
+ case NO_UPDATE:
+ if (master_entries_completed_ > 0) {
+ switch (stored_state_) {
+ case UNSTORED:
+ StoreGroupAndCache();
+ return;
+ case STORING:
+ return;
+ case STORED:
+ break;
+ }
+ }
+ // 6.9.4 steps 7.3-7.7.
+ NotifyAllAssociatedHosts(APPCACHE_NO_UPDATE_EVENT);
+ DiscardDuplicateResponses();
+ internal_state_ = COMPLETED;
+ break;
+ case DOWNLOADING:
+ internal_state_ = REFETCH_MANIFEST;
+ FetchManifest(false);
+ break;
+ case REFETCH_MANIFEST:
+ DCHECK(stored_state_ == STORED);
+ NotifyAllFinalProgress();
+ if (update_type_ == CACHE_ATTEMPT)
+ NotifyAllAssociatedHosts(APPCACHE_CACHED_EVENT);
+ else
+ NotifyAllAssociatedHosts(APPCACHE_UPDATE_READY_EVENT);
+ DiscardDuplicateResponses();
+ internal_state_ = COMPLETED;
+ LogHistogramStats(UPDATE_OK, GURL());
+ break;
+ case CACHE_FAILURE:
+ NOTREACHED(); // See HandleCacheFailure
+ break;
+ default:
+ break;
+ }
+
+ // Let the stack unwind before deletion to make it less risky as this
+ // method is called from multiple places in this file.
+ if (internal_state_ == COMPLETED)
+ DeleteSoon();
+}
+
+void AppCacheUpdateJob::ScheduleUpdateRetry(int delay_ms) {
+ // TODO(jennb): post a delayed task with the "same parameters" as this job
+ // to retry the update at a later time. Need group, URLs of pending master
+ // entries and their hosts.
+}
+
+void AppCacheUpdateJob::Cancel() {
+ internal_state_ = CANCELLED;
+
+ LogHistogramStats(CANCELLED_ERROR, GURL());
+
+ if (manifest_fetcher_) {
+ delete manifest_fetcher_;
+ manifest_fetcher_ = NULL;
+ }
+
+ for (PendingUrlFetches::iterator it = pending_url_fetches_.begin();
+ it != pending_url_fetches_.end(); ++it) {
+ delete it->second;
+ }
+ pending_url_fetches_.clear();
+
+ for (PendingUrlFetches::iterator it = master_entry_fetches_.begin();
+ it != master_entry_fetches_.end(); ++it) {
+ delete it->second;
+ }
+ master_entry_fetches_.clear();
+
+ ClearPendingMasterEntries();
+ DiscardInprogressCache();
+
+ // Delete response writer to avoid any callbacks.
+ if (manifest_response_writer_)
+ manifest_response_writer_.reset();
+
+ storage_->CancelDelegateCallbacks(this);
+}
+
+void AppCacheUpdateJob::ClearPendingMasterEntries() {
+ for (PendingMasters::iterator it = pending_master_entries_.begin();
+ it != pending_master_entries_.end(); ++it) {
+ PendingHosts& hosts = it->second;
+ for (PendingHosts::iterator host_it = hosts.begin();
+ host_it != hosts.end(); ++host_it) {
+ (*host_it)->RemoveObserver(this);
+ }
+ }
+
+ pending_master_entries_.clear();
+}
+
+void AppCacheUpdateJob::DiscardInprogressCache() {
+ if (stored_state_ == STORING) {
+ // We can make no assumptions about whether the StoreGroupAndCacheTask
+ // actually completed or not. This condition should only be reachable
+ // during shutdown. Free things up and return to do no harm.
+ inprogress_cache_ = NULL;
+ added_master_entries_.clear();
+ return;
+ }
+
+ storage_->DoomResponses(manifest_url_, stored_response_ids_);
+
+ if (!inprogress_cache_.get()) {
+ // We have to undo the changes we made, if any, to the existing cache.
+ if (group_ && group_->newest_complete_cache()) {
+ for (std::vector<GURL>::iterator iter = added_master_entries_.begin();
+ iter != added_master_entries_.end(); ++iter) {
+ group_->newest_complete_cache()->RemoveEntry(*iter);
+ }
+ }
+ added_master_entries_.clear();
+ return;
+ }
+
+ AppCache::AppCacheHosts& hosts = inprogress_cache_->associated_hosts();
+ while (!hosts.empty())
+ (*hosts.begin())->AssociateNoCache(GURL());
+
+ inprogress_cache_ = NULL;
+ added_master_entries_.clear();
+}
+
+void AppCacheUpdateJob::DiscardDuplicateResponses() {
+ storage_->DoomResponses(manifest_url_, duplicate_response_ids_);
+}
+
+void AppCacheUpdateJob::LogHistogramStats(
+ ResultType result, const GURL& failed_resource_url) {
+ AppCacheHistograms::CountUpdateJobResult(result, manifest_url_.GetOrigin());
+ if (result == UPDATE_OK)
+ return;
+
+ int percent_complete = 0;
+ if (url_file_list_.size() > 0) {
+ size_t actual_fetches_completed = url_fetches_completed_;
+ if (!failed_resource_url.is_empty() && actual_fetches_completed)
+ --actual_fetches_completed;
+ percent_complete = (static_cast<double>(actual_fetches_completed) /
+ static_cast<double>(url_file_list_.size())) * 100.0;
+ percent_complete = std::min(percent_complete, 99);
+ }
+
+ bool was_making_progress =
+ base::Time::Now() - last_progress_time_ <
+ base::TimeDelta::FromMinutes(5);
+
+ bool off_origin_resource_failure =
+ !failed_resource_url.is_empty() &&
+ (failed_resource_url.GetOrigin() != manifest_url_.GetOrigin());
+
+ AppCacheHistograms::LogUpdateFailureStats(
+ manifest_url_.GetOrigin(),
+ percent_complete,
+ was_making_progress,
+ off_origin_resource_failure);
+}
+
+void AppCacheUpdateJob::DeleteSoon() {
+ ClearPendingMasterEntries();
+ manifest_response_writer_.reset();
+ storage_->CancelDelegateCallbacks(this);
+ service_->RemoveObserver(this);
+ service_ = NULL;
+
+ // Break the connection with the group so the group cannot call delete
+ // on this object after we've posted a task to delete ourselves.
+ group_->SetUpdateAppCacheStatus(AppCacheGroup::IDLE);
+ group_ = NULL;
+
+ base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+}
+
+} // namespace content
diff --git a/content/browser/appcache/appcache_update_job.h b/content/browser/appcache/appcache_update_job.h
new file mode 100644
index 0000000..14fc831
--- /dev/null
+++ b/content/browser/appcache/appcache_update_job.h
@@ -0,0 +1,352 @@
+// Copyright (c) 2012 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.
+
+#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_UPDATE_JOB_H_
+#define CONTENT_BROWSER_APPCACHE_APPCACHE_UPDATE_JOB_H_
+
+#include <deque>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+#include "content/browser/appcache/appcache.h"
+#include "content/browser/appcache/appcache_host.h"
+#include "content/browser/appcache/appcache_response.h"
+#include "content/browser/appcache/appcache_service_impl.h"
+#include "content/browser/appcache/appcache_storage.h"
+#include "content/common/appcache_interfaces.h"
+#include "content/common/content_export.h"
+#include "net/base/completion_callback.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_request.h"
+#include "url/gurl.h"
+
+namespace content {
+FORWARD_DECLARE_TEST(AppCacheGroupTest, QueueUpdate);
+class AppCacheGroupTest;
+class AppCacheUpdateJobTest;
+}
+
+namespace content {
+
+class HostNotifier;
+
+// Application cache Update algorithm and state.
+class CONTENT_EXPORT AppCacheUpdateJob
+ : public AppCacheStorage::Delegate,
+ public AppCacheHost::Observer,
+ public AppCacheServiceImpl::Observer {
+ public:
+ // Used for uma stats only for now, so new values are append only.
+ enum ResultType {
+ UPDATE_OK, DB_ERROR, DISKCACHE_ERROR, QUOTA_ERROR, REDIRECT_ERROR,
+ MANIFEST_ERROR, NETWORK_ERROR, SERVER_ERROR, CANCELLED_ERROR,
+ NUM_UPDATE_JOB_RESULT_TYPES
+ };
+
+ AppCacheUpdateJob(AppCacheServiceImpl* service, AppCacheGroup* group);
+ virtual ~AppCacheUpdateJob();
+
+ // Triggers the update process or adds more info if this update is already
+ // in progress.
+ void StartUpdate(AppCacheHost* host, const GURL& new_master_resource);
+
+ private:
+ friend class content::AppCacheGroupTest;
+ friend class content::AppCacheUpdateJobTest;
+ class URLFetcher;
+
+ // Master entries have multiple hosts, for example, the same page is opened
+ // in different tabs.
+ typedef std::vector<AppCacheHost*> PendingHosts;
+ typedef std::map<GURL, PendingHosts> PendingMasters;
+ typedef std::map<GURL, URLFetcher*> PendingUrlFetches;
+ typedef std::map<int64, GURL> LoadingResponses;
+
+ static const int kRerunDelayMs = 1000;
+
+ // TODO(michaeln): Rework the set of states vs update types vs stored states.
+ // The NO_UPDATE state is really more of an update type. For all update types
+ // storing the results is relevant.
+
+ enum UpdateType {
+ UNKNOWN_TYPE,
+ UPGRADE_ATTEMPT,
+ CACHE_ATTEMPT,
+ };
+
+ enum InternalUpdateState {
+ FETCH_MANIFEST,
+ NO_UPDATE,
+ DOWNLOADING,
+
+ // Every state after this comment indicates the update is terminating.
+ REFETCH_MANIFEST,
+ CACHE_FAILURE,
+ CANCELLED,
+ COMPLETED,
+ };
+
+ enum StoredState {
+ UNSTORED,
+ STORING,
+ STORED,
+ };
+
+ struct UrlToFetch {
+ UrlToFetch(const GURL& url, bool checked, AppCacheResponseInfo* info);
+ ~UrlToFetch();
+
+ GURL url;
+ bool storage_checked;
+ scoped_refptr<AppCacheResponseInfo> existing_response_info;
+ };
+
+ class URLFetcher : public net::URLRequest::Delegate {
+ public:
+ enum FetchType {
+ MANIFEST_FETCH,
+ URL_FETCH,
+ MASTER_ENTRY_FETCH,
+ MANIFEST_REFETCH,
+ };
+ URLFetcher(const GURL& url,
+ FetchType fetch_type,
+ AppCacheUpdateJob* job);
+ virtual ~URLFetcher();
+ void Start();
+ FetchType fetch_type() const { return fetch_type_; }
+ net::URLRequest* request() const { return request_.get(); }
+ const AppCacheEntry& existing_entry() const { return existing_entry_; }
+ const std::string& manifest_data() const { return manifest_data_; }
+ AppCacheResponseWriter* response_writer() const {
+ return response_writer_.get();
+ }
+ void set_existing_response_headers(net::HttpResponseHeaders* headers) {
+ existing_response_headers_ = headers;
+ }
+ void set_existing_entry(const AppCacheEntry& entry) {
+ existing_entry_ = entry;
+ }
+ ResultType result() const { return result_; }
+ int redirect_response_code() const { return redirect_response_code_; }
+
+ private:
+ // URLRequest::Delegate overrides
+ virtual void OnReceivedRedirect(net::URLRequest* request,
+ const GURL& new_url,
+ bool* defer_redirect) OVERRIDE;
+ virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE;
+ virtual void OnReadCompleted(net::URLRequest* request,
+ int bytes_read) OVERRIDE;
+
+ void AddConditionalHeaders(const net::HttpResponseHeaders* headers);
+ void OnWriteComplete(int result);
+ void ReadResponseData();
+ bool ConsumeResponseData(int bytes_read);
+ void OnResponseCompleted();
+ bool MaybeRetryRequest();
+
+ GURL url_;
+ AppCacheUpdateJob* job_;
+ FetchType fetch_type_;
+ int retry_503_attempts_;
+ scoped_refptr<net::IOBuffer> buffer_;
+ scoped_ptr<net::URLRequest> request_;
+ AppCacheEntry existing_entry_;
+ scoped_refptr<net::HttpResponseHeaders> existing_response_headers_;
+ std::string manifest_data_;
+ ResultType result_;
+ int redirect_response_code_;
+ scoped_ptr<AppCacheResponseWriter> response_writer_;
+ }; // class URLFetcher
+
+ AppCacheResponseWriter* CreateResponseWriter();
+
+ // Methods for AppCacheStorage::Delegate.
+ virtual void OnResponseInfoLoaded(AppCacheResponseInfo* response_info,
+ int64 response_id) OVERRIDE;
+ virtual void OnGroupAndNewestCacheStored(AppCacheGroup* group,
+ AppCache* newest_cache,
+ bool success,
+ bool would_exceed_quota) OVERRIDE;
+ virtual void OnGroupMadeObsolete(AppCacheGroup* group,
+ bool success,
+ int response_code) OVERRIDE;
+
+ // Methods for AppCacheHost::Observer.
+ virtual void OnCacheSelectionComplete(AppCacheHost* host) OVERRIDE {} // N/A
+ virtual void OnDestructionImminent(AppCacheHost* host) OVERRIDE;
+
+ // Methods for AppCacheServiceImpl::Observer.
+ virtual void OnServiceReinitialized(
+ AppCacheStorageReference* old_storage) OVERRIDE;
+
+ void HandleCacheFailure(const AppCacheErrorDetails& details,
+ ResultType result,
+ const GURL& failed_resource_url);
+
+ void FetchManifest(bool is_first_fetch);
+ void HandleManifestFetchCompleted(URLFetcher* fetcher);
+ void ContinueHandleManifestFetchCompleted(bool changed);
+
+ void HandleUrlFetchCompleted(URLFetcher* fetcher);
+ void HandleMasterEntryFetchCompleted(URLFetcher* fetcher);
+
+ void HandleManifestRefetchCompleted(URLFetcher* fetcher);
+ void OnManifestInfoWriteComplete(int result);
+ void OnManifestDataWriteComplete(int result);
+
+ void StoreGroupAndCache();
+
+ void NotifySingleHost(AppCacheHost* host, AppCacheEventID event_id);
+ void NotifyAllAssociatedHosts(AppCacheEventID event_id);
+ void NotifyAllProgress(const GURL& url);
+ void NotifyAllFinalProgress();
+ void NotifyAllError(const AppCacheErrorDetails& detals);
+ void LogConsoleMessageToAll(const std::string& message);
+ void AddAllAssociatedHostsToNotifier(HostNotifier* notifier);
+
+ // Checks if manifest is byte for byte identical with the manifest
+ // in the newest application cache.
+ void CheckIfManifestChanged();
+ void OnManifestDataReadComplete(int result);
+
+ // Creates the list of files that may need to be fetched and initiates
+ // fetches. Section 6.9.4 steps 12-17
+ void BuildUrlFileList(const Manifest& manifest);
+ void AddUrlToFileList(const GURL& url, int type);
+ void FetchUrls();
+ void CancelAllUrlFetches();
+ bool ShouldSkipUrlFetch(const AppCacheEntry& entry);
+
+ // If entry already exists in the cache currently being updated, merge
+ // the entry type information with the existing entry.
+ // Returns true if entry exists in cache currently being updated.
+ bool AlreadyFetchedEntry(const GURL& url, int entry_type);
+
+ // TODO(jennb): Delete when update no longer fetches master entries directly.
+ // Creates the list of master entries that need to be fetched and initiates
+ // fetches.
+ void AddMasterEntryToFetchList(AppCacheHost* host, const GURL& url,
+ bool is_new);
+ void FetchMasterEntries();
+ void CancelAllMasterEntryFetches(const AppCacheErrorDetails& details);
+
+ // Asynchronously loads the entry from the newest complete cache if the
+ // HTTP caching semantics allow.
+ // Returns false if immediately obvious that data cannot be loaded from
+ // newest complete cache.
+ bool MaybeLoadFromNewestCache(const GURL& url, AppCacheEntry& entry);
+ void LoadFromNewestCacheFailed(const GURL& url,
+ AppCacheResponseInfo* newest_response_info);
+
+ // Does nothing if update process is still waiting for pending master
+ // entries or URL fetches to complete downloading. Otherwise, completes
+ // the update process.
+ void MaybeCompleteUpdate();
+
+ // Schedules a rerun of the entire update with the same parameters as
+ // this update job after a short delay.
+ void ScheduleUpdateRetry(int delay_ms);
+
+ void Cancel();
+ void ClearPendingMasterEntries();
+ void DiscardInprogressCache();
+ void DiscardDuplicateResponses();
+
+ void LogHistogramStats(ResultType result, const GURL& failed_resource_url);
+ void MadeProgress() { last_progress_time_ = base::Time::Now(); }
+
+ // Deletes this object after letting the stack unwind.
+ void DeleteSoon();
+
+ bool IsTerminating() { return internal_state_ >= REFETCH_MANIFEST ||
+ stored_state_ != UNSTORED; }
+
+ AppCacheServiceImpl* service_;
+ const GURL manifest_url_; // here for easier access
+
+ // Defined prior to refs to AppCaches and Groups because destruction
+ // order matters, the disabled_storage_reference_ must outlive those
+ // objects.
+ scoped_refptr<AppCacheStorageReference> disabled_storage_reference_;
+
+ scoped_refptr<AppCache> inprogress_cache_;
+
+ AppCacheGroup* group_;
+
+ UpdateType update_type_;
+ InternalUpdateState internal_state_;
+ base::Time last_progress_time_;
+
+ PendingMasters pending_master_entries_;
+ size_t master_entries_completed_;
+
+ // TODO(jennb): Delete when update no longer fetches master entries directly.
+ // Helper containers to track which pending master entries have yet to be
+ // fetched and which are currently being fetched. Master entries that
+ // are listed in the manifest may be fetched as a regular URL instead of
+ // as a separate master entry fetch to optimize against duplicate fetches.
+ std::set<GURL> master_entries_to_fetch_;
+ PendingUrlFetches master_entry_fetches_;
+
+ // URLs of files to fetch along with their flags.
+ AppCache::EntryMap url_file_list_;
+ size_t url_fetches_completed_;
+
+ // Helper container to track which urls have not been fetched yet. URLs are
+ // removed when the fetch is initiated. Flag indicates whether an attempt
+ // to load the URL from storage has already been tried and failed.
+ std::deque<UrlToFetch> urls_to_fetch_;
+
+ // Helper container to track which urls are being loaded from response
+ // storage.
+ LoadingResponses loading_responses_;
+
+ // Keep track of pending URL requests so we can cancel them if necessary.
+ URLFetcher* manifest_fetcher_;
+ PendingUrlFetches pending_url_fetches_;
+
+ // Temporary storage of manifest response data for parsing and comparison.
+ std::string manifest_data_;
+ scoped_ptr<net::HttpResponseInfo> manifest_response_info_;
+ scoped_ptr<AppCacheResponseWriter> manifest_response_writer_;
+ scoped_refptr<net::IOBuffer> read_manifest_buffer_;
+ std::string loaded_manifest_data_;
+ scoped_ptr<AppCacheResponseReader> manifest_response_reader_;
+ bool manifest_has_valid_mime_type_;
+
+ // New master entries added to the cache by this job, used to cleanup
+ // in error conditions.
+ std::vector<GURL> added_master_entries_;
+
+ // Response ids stored by this update job, used to cleanup in
+ // error conditions.
+ std::vector<int64> stored_response_ids_;
+
+ // In some cases we fetch the same resource multiple times, and then
+ // have to delete the duplicates upon successful update. These ids
+ // are also in the stored_response_ids_ collection so we only schedule
+ // these for deletion on success.
+ // TODO(michaeln): Rework when we no longer fetches master entries directly.
+ std::vector<int64> duplicate_response_ids_;
+
+ // Whether we've stored the resulting group/cache yet.
+ StoredState stored_state_;
+
+ AppCacheStorage* storage_;
+
+ FRIEND_TEST_ALL_PREFIXES(content::AppCacheGroupTest, QueueUpdate);
+
+ DISALLOW_COPY_AND_ASSIGN(AppCacheUpdateJob);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_APPCACHE_APPCACHE_UPDATE_JOB_H_
diff --git a/content/browser/appcache/appcache_update_job_unittest.cc b/content/browser/appcache/appcache_update_job_unittest.cc
index bc3957e..6eb4e02 100644
--- a/content/browser/appcache/appcache_update_job_unittest.cc
+++ b/content/browser/appcache/appcache_update_job_unittest.cc
@@ -7,6 +7,10 @@
#include "base/stl_util.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
+#include "content/browser/appcache/appcache_group.h"
+#include "content/browser/appcache/appcache_host.h"
+#include "content/browser/appcache/appcache_response.h"
+#include "content/browser/appcache/appcache_update_job.h"
#include "content/browser/appcache/mock_appcache_service.h"
#include "net/base/net_errors.h"
#include "net/http/http_response_headers.h"
@@ -15,35 +19,6 @@
#include "net/url_request/url_request_test_job.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/appcache/appcache_group.h"
-#include "webkit/browser/appcache/appcache_host.h"
-#include "webkit/browser/appcache/appcache_response.h"
-#include "webkit/browser/appcache/appcache_update_job.h"
-
-using appcache::AppCache;
-using appcache::AppCacheEntry;
-using appcache::AppCacheFrontend;
-using appcache::AppCacheHost;
-using appcache::AppCacheGroup;
-using appcache::AppCacheResponseInfo;
-using appcache::AppCacheUpdateJob;
-using appcache::AppCacheResponseWriter;
-using appcache::APPCACHE_CACHED_EVENT;
-using appcache::APPCACHE_CHECKING_EVENT;
-using appcache::APPCACHE_DOWNLOADING_EVENT;
-using appcache::APPCACHE_ERROR_EVENT;
-using appcache::AppCacheEventID;
-using appcache::APPCACHE_FALLBACK_NAMESPACE;
-using appcache::HttpResponseInfoIOBuffer;
-using appcache::kAppCacheNoCacheId;
-using appcache::kAppCacheNoResponseId;
-using appcache::Namespace;
-using appcache::APPCACHE_NETWORK_NAMESPACE;
-using appcache::APPCACHE_NO_UPDATE_EVENT;
-using appcache::APPCACHE_OBSOLETE_EVENT;
-using appcache::APPCACHE_PROGRESS_EVENT;
-using appcache::APPCACHE_UPDATE_READY_EVENT;
-using appcache::AppCacheStatus;
namespace content {
class AppCacheUpdateJobTest;
@@ -227,7 +202,8 @@ class MockHttpServerJobFactory
}
};
-inline bool operator==(const Namespace& lhs, const Namespace& rhs) {
+inline bool operator==(const AppCacheNamespace& lhs,
+ const AppCacheNamespace& rhs) {
return lhs.type == rhs.type &&
lhs.namespace_url == rhs.namespace_url &&
lhs.target_url == rhs.target_url;
@@ -244,7 +220,7 @@ class MockFrontend : public AppCacheFrontend {
}
virtual void OnCacheSelected(
- int host_id, const appcache::AppCacheInfo& info) OVERRIDE {
+ int host_id, const AppCacheInfo& info) OVERRIDE {
}
virtual void OnStatusChanged(const std::vector<int>& host_ids,
@@ -268,7 +244,7 @@ class MockFrontend : public AppCacheFrontend {
}
virtual void OnErrorEventRaised(const std::vector<int>& host_ids,
- const appcache::AppCacheErrorDetails& details)
+ const AppCacheErrorDetails& details)
OVERRIDE {
error_message_ = details.message;
OnEventRaised(host_ids, APPCACHE_ERROR_EVENT);
@@ -306,7 +282,7 @@ class MockFrontend : public AppCacheFrontend {
}
virtual void OnLogMessage(int host_id,
- appcache::AppCacheLogLevel log_level,
+ AppCacheLogLevel log_level,
const std::string& message) OVERRIDE {
}
@@ -3273,7 +3249,7 @@ class AppCacheUpdateJobTest : public testing::Test,
expected = 1;
ASSERT_EQ(expected, cache->fallback_namespaces_.size());
EXPECT_TRUE(cache->fallback_namespaces_[0] ==
- Namespace(
+ AppCacheNamespace(
APPCACHE_FALLBACK_NAMESPACE,
MockHttpServer::GetMockUrl("files/fallback1"),
MockHttpServer::GetMockUrl("files/fallback1a"),
@@ -3301,7 +3277,7 @@ class AppCacheUpdateJobTest : public testing::Test,
expected = 1;
ASSERT_EQ(expected, cache->fallback_namespaces_.size());
EXPECT_TRUE(cache->fallback_namespaces_[0] ==
- Namespace(
+ AppCacheNamespace(
APPCACHE_FALLBACK_NAMESPACE,
MockHttpServer::GetMockUrl("files/fallback1"),
MockHttpServer::GetMockUrl("files/explicit1"),
@@ -3309,7 +3285,7 @@ class AppCacheUpdateJobTest : public testing::Test,
EXPECT_EQ(expected, cache->online_whitelist_namespaces_.size());
EXPECT_TRUE(cache->online_whitelist_namespaces_[0] ==
- Namespace(
+ AppCacheNamespace(
APPCACHE_NETWORK_NAMESPACE,
MockHttpServer::GetMockUrl("files/online1"),
GURL(), false));
@@ -3498,7 +3474,7 @@ TEST_F(AppCacheUpdateJobTest, AlreadyDownloading) {
EXPECT_EQ(expected, events[1].first.size());
EXPECT_EQ(host.host_id(), events[1].first[0]);
- EXPECT_EQ(appcache::APPCACHE_DOWNLOADING_EVENT, events[1].second);
+ EXPECT_EQ(APPCACHE_DOWNLOADING_EVENT, events[1].second);
EXPECT_EQ(AppCacheGroup::DOWNLOADING, group->update_status());
}
diff --git a/content/browser/appcache/appcache_url_request_job.cc b/content/browser/appcache/appcache_url_request_job.cc
new file mode 100644
index 0000000..b12a5b6
--- /dev/null
+++ b/content/browser/appcache/appcache_url_request_job.cc
@@ -0,0 +1,449 @@
+// Copyright (c) 2012 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 "content/browser/appcache/appcache_url_request_job.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "content/browser/appcache/appcache.h"
+#include "content/browser/appcache/appcache_group.h"
+#include "content/browser/appcache/appcache_histograms.h"
+#include "content/browser/appcache/appcache_host.h"
+#include "content/browser/appcache/appcache_service_impl.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_util.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_status.h"
+
+namespace content {
+
+AppCacheURLRequestJob::AppCacheURLRequestJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ AppCacheStorage* storage,
+ AppCacheHost* host,
+ bool is_main_resource)
+ : net::URLRequestJob(request, network_delegate),
+ host_(host),
+ storage_(storage),
+ has_been_started_(false), has_been_killed_(false),
+ delivery_type_(AWAITING_DELIVERY_ORDERS),
+ group_id_(0), cache_id_(kAppCacheNoCacheId), is_fallback_(false),
+ is_main_resource_(is_main_resource),
+ cache_entry_not_found_(false),
+ weak_factory_(this) {
+ DCHECK(storage_);
+}
+
+void AppCacheURLRequestJob::DeliverAppCachedResponse(
+ const GURL& manifest_url, int64 group_id, int64 cache_id,
+ const AppCacheEntry& entry, bool is_fallback) {
+ DCHECK(!has_delivery_orders());
+ DCHECK(entry.has_response_id());
+ delivery_type_ = APPCACHED_DELIVERY;
+ manifest_url_ = manifest_url;
+ group_id_ = group_id;
+ cache_id_ = cache_id;
+ entry_ = entry;
+ is_fallback_ = is_fallback;
+ MaybeBeginDelivery();
+}
+
+void AppCacheURLRequestJob::DeliverNetworkResponse() {
+ DCHECK(!has_delivery_orders());
+ delivery_type_ = NETWORK_DELIVERY;
+ storage_ = NULL; // not needed
+ MaybeBeginDelivery();
+}
+
+void AppCacheURLRequestJob::DeliverErrorResponse() {
+ DCHECK(!has_delivery_orders());
+ delivery_type_ = ERROR_DELIVERY;
+ storage_ = NULL; // not needed
+ MaybeBeginDelivery();
+}
+
+void AppCacheURLRequestJob::MaybeBeginDelivery() {
+ if (has_been_started() && has_delivery_orders()) {
+ // Start asynchronously so that all error reporting and data
+ // callbacks happen as they would for network requests.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&AppCacheURLRequestJob::BeginDelivery,
+ weak_factory_.GetWeakPtr()));
+ }
+}
+
+void AppCacheURLRequestJob::BeginDelivery() {
+ DCHECK(has_delivery_orders() && has_been_started());
+
+ if (has_been_killed())
+ return;
+
+ switch (delivery_type_) {
+ case NETWORK_DELIVERY:
+ AppCacheHistograms::AddNetworkJobStartDelaySample(
+ base::TimeTicks::Now() - start_time_tick_);
+ // To fallthru to the network, we restart the request which will
+ // cause a new job to be created to retrieve the resource from the
+ // network. Our caller is responsible for arranging to not re-intercept
+ // the same request.
+ NotifyRestartRequired();
+ break;
+
+ case ERROR_DELIVERY:
+ AppCacheHistograms::AddErrorJobStartDelaySample(
+ base::TimeTicks::Now() - start_time_tick_);
+ request()->net_log().AddEvent(
+ net::NetLog::TYPE_APPCACHE_DELIVERING_ERROR_RESPONSE);
+ NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
+ net::ERR_FAILED));
+ break;
+
+ case APPCACHED_DELIVERY:
+ if (entry_.IsExecutable()) {
+ BeginExecutableHandlerDelivery();
+ return;
+ }
+ AppCacheHistograms::AddAppCacheJobStartDelaySample(
+ base::TimeTicks::Now() - start_time_tick_);
+ request()->net_log().AddEvent(
+ is_fallback_ ?
+ net::NetLog::TYPE_APPCACHE_DELIVERING_FALLBACK_RESPONSE :
+ net::NetLog::TYPE_APPCACHE_DELIVERING_CACHED_RESPONSE);
+ storage_->LoadResponseInfo(
+ manifest_url_, group_id_, entry_.response_id(), this);
+ break;
+
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+void AppCacheURLRequestJob::BeginExecutableHandlerDelivery() {
+ DCHECK(CommandLine::ForCurrentProcess()->
+ HasSwitch(kEnableExecutableHandlers));
+ if (!storage_->service()->handler_factory()) {
+ BeginErrorDelivery("missing handler factory");
+ return;
+ }
+
+ request()->net_log().AddEvent(
+ net::NetLog::TYPE_APPCACHE_DELIVERING_EXECUTABLE_RESPONSE);
+
+ // We defer job delivery until the executable handler is spun up and
+ // provides a response. The sequence goes like this...
+ //
+ // 1. First we load the cache.
+ // 2. Then if the handler is not spun up, we load the script resource which
+ // is needed to spin it up.
+ // 3. Then we ask then we ask the handler to compute a response.
+ // 4. Finally we deilver that response to the caller.
+ storage_->LoadCache(cache_id_, this);
+}
+
+void AppCacheURLRequestJob::OnCacheLoaded(AppCache* cache, int64 cache_id) {
+ DCHECK_EQ(cache_id_, cache_id);
+ DCHECK(!has_been_killed());
+
+ if (!cache) {
+ BeginErrorDelivery("cache load failed");
+ return;
+ }
+
+ // Keep references to ensure they don't go out of scope until job completion.
+ cache_ = cache;
+ group_ = cache->owning_group();
+
+ // If the handler is spun up, ask it to compute a response.
+ AppCacheExecutableHandler* handler =
+ cache->GetExecutableHandler(entry_.response_id());
+ if (handler) {
+ InvokeExecutableHandler(handler);
+ return;
+ }
+
+ // Handler is not spun up yet, load the script resource to do that.
+ // NOTE: This is not ideal since multiple jobs may be doing this,
+ // concurrently but close enough for now, the first to load the script
+ // will win.
+
+ // Read the script data, truncating if its too large.
+ // NOTE: we just issue one read and don't bother chaining if the resource
+ // is very (very) large, close enough for now.
+ const int64 kLimit = 500 * 1000;
+ handler_source_buffer_ = new net::GrowableIOBuffer();
+ handler_source_buffer_->SetCapacity(kLimit);
+ handler_source_reader_.reset(storage_->CreateResponseReader(
+ manifest_url_, group_id_, entry_.response_id()));
+ handler_source_reader_->ReadData(
+ handler_source_buffer_.get(),
+ kLimit,
+ base::Bind(&AppCacheURLRequestJob::OnExecutableSourceLoaded,
+ base::Unretained(this)));
+}
+
+void AppCacheURLRequestJob::OnExecutableSourceLoaded(int result) {
+ DCHECK(!has_been_killed());
+ handler_source_reader_.reset();
+ if (result < 0) {
+ BeginErrorDelivery("script source load failed");
+ return;
+ }
+
+ handler_source_buffer_->SetCapacity(result); // Free up some memory.
+
+ AppCacheExecutableHandler* handler = cache_->GetOrCreateExecutableHandler(
+ entry_.response_id(), handler_source_buffer_.get());
+ handler_source_buffer_ = NULL; // not needed anymore
+ if (handler) {
+ InvokeExecutableHandler(handler);
+ return;
+ }
+
+ BeginErrorDelivery("factory failed to produce a handler");
+}
+
+void AppCacheURLRequestJob::InvokeExecutableHandler(
+ AppCacheExecutableHandler* handler) {
+ handler->HandleRequest(
+ request(),
+ base::Bind(&AppCacheURLRequestJob::OnExecutableResponseCallback,
+ weak_factory_.GetWeakPtr()));
+}
+
+void AppCacheURLRequestJob::OnExecutableResponseCallback(
+ const AppCacheExecutableHandler::Response& response) {
+ DCHECK(!has_been_killed());
+ if (response.use_network) {
+ delivery_type_ = NETWORK_DELIVERY;
+ storage_ = NULL;
+ BeginDelivery();
+ return;
+ }
+
+ if (!response.cached_resource_url.is_empty()) {
+ AppCacheEntry* entry_ptr = cache_->GetEntry(response.cached_resource_url);
+ if (entry_ptr && !entry_ptr->IsExecutable()) {
+ entry_ = *entry_ptr;
+ BeginDelivery();
+ return;
+ }
+ }
+
+ if (!response.redirect_url.is_empty()) {
+ // TODO(michaeln): playback a redirect
+ // response_headers_(new HttpResponseHeaders(response_headers)),
+ // fallthru for now to deliver an error
+ }
+
+ // Otherwise, return an error.
+ BeginErrorDelivery("handler returned an invalid response");
+}
+
+void AppCacheURLRequestJob::BeginErrorDelivery(const char* message) {
+ if (host_)
+ host_->frontend()->OnLogMessage(host_->host_id(), APPCACHE_LOG_ERROR,
+ message);
+ delivery_type_ = ERROR_DELIVERY;
+ storage_ = NULL;
+ BeginDelivery();
+}
+
+AppCacheURLRequestJob::~AppCacheURLRequestJob() {
+ if (storage_)
+ storage_->CancelDelegateCallbacks(this);
+}
+
+void AppCacheURLRequestJob::OnResponseInfoLoaded(
+ AppCacheResponseInfo* response_info, int64 response_id) {
+ DCHECK(is_delivering_appcache_response());
+ scoped_refptr<AppCacheURLRequestJob> protect(this);
+ if (response_info) {
+ info_ = response_info;
+ reader_.reset(storage_->CreateResponseReader(
+ manifest_url_, group_id_, entry_.response_id()));
+
+ if (is_range_request())
+ SetupRangeResponse();
+
+ NotifyHeadersComplete();
+ } else {
+ if (storage_->service()->storage() == storage_) {
+ // A resource that is expected to be in the appcache is missing.
+ // See http://code.google.com/p/chromium/issues/detail?id=50657
+ // Instead of failing the request, we restart the request. The retry
+ // attempt will fallthru to the network instead of trying to load
+ // from the appcache.
+ storage_->service()->CheckAppCacheResponse(manifest_url_, cache_id_,
+ entry_.response_id());
+ AppCacheHistograms::CountResponseRetrieval(
+ false, is_main_resource_, manifest_url_.GetOrigin());
+ }
+ cache_entry_not_found_ = true;
+ NotifyRestartRequired();
+ }
+}
+
+const net::HttpResponseInfo* AppCacheURLRequestJob::http_info() const {
+ if (!info_.get())
+ return NULL;
+ if (range_response_info_)
+ return range_response_info_.get();
+ return info_->http_response_info();
+}
+
+void AppCacheURLRequestJob::SetupRangeResponse() {
+ DCHECK(is_range_request() && info_.get() && reader_.get() &&
+ is_delivering_appcache_response());
+ int resource_size = static_cast<int>(info_->response_data_size());
+ if (resource_size < 0 || !range_requested_.ComputeBounds(resource_size)) {
+ range_requested_ = net::HttpByteRange();
+ return;
+ }
+
+ DCHECK(range_requested_.IsValid());
+ int offset = static_cast<int>(range_requested_.first_byte_position());
+ int length = static_cast<int>(range_requested_.last_byte_position() -
+ range_requested_.first_byte_position() + 1);
+
+ // Tell the reader about the range to read.
+ reader_->SetReadRange(offset, length);
+
+ // Make a copy of the full response headers and fix them up
+ // for the range we'll be returning.
+ range_response_info_.reset(
+ new net::HttpResponseInfo(*info_->http_response_info()));
+ net::HttpResponseHeaders* headers = range_response_info_->headers.get();
+ headers->UpdateWithNewRange(
+ range_requested_, resource_size, true /* replace status line */);
+}
+
+void AppCacheURLRequestJob::OnReadComplete(int result) {
+ DCHECK(is_delivering_appcache_response());
+ if (result == 0) {
+ NotifyDone(net::URLRequestStatus());
+ AppCacheHistograms::CountResponseRetrieval(
+ true, is_main_resource_, manifest_url_.GetOrigin());
+ } else if (result < 0) {
+ if (storage_->service()->storage() == storage_) {
+ storage_->service()->CheckAppCacheResponse(manifest_url_, cache_id_,
+ entry_.response_id());
+ }
+ NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, result));
+ AppCacheHistograms::CountResponseRetrieval(
+ false, is_main_resource_, manifest_url_.GetOrigin());
+ } else {
+ SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status
+ }
+ NotifyReadComplete(result);
+}
+
+// net::URLRequestJob overrides ------------------------------------------------
+
+void AppCacheURLRequestJob::Start() {
+ DCHECK(!has_been_started());
+ has_been_started_ = true;
+ start_time_tick_ = base::TimeTicks::Now();
+ MaybeBeginDelivery();
+}
+
+void AppCacheURLRequestJob::Kill() {
+ if (!has_been_killed_) {
+ has_been_killed_ = true;
+ reader_.reset();
+ handler_source_reader_.reset();
+ if (storage_) {
+ storage_->CancelDelegateCallbacks(this);
+ storage_ = NULL;
+ }
+ host_ = NULL;
+ info_ = NULL;
+ cache_ = NULL;
+ group_ = NULL;
+ range_response_info_.reset();
+ net::URLRequestJob::Kill();
+ weak_factory_.InvalidateWeakPtrs();
+ }
+}
+
+net::LoadState AppCacheURLRequestJob::GetLoadState() const {
+ if (!has_been_started())
+ return net::LOAD_STATE_IDLE;
+ if (!has_delivery_orders())
+ return net::LOAD_STATE_WAITING_FOR_APPCACHE;
+ if (delivery_type_ != APPCACHED_DELIVERY)
+ return net::LOAD_STATE_IDLE;
+ if (!info_.get())
+ return net::LOAD_STATE_WAITING_FOR_APPCACHE;
+ if (reader_.get() && reader_->IsReadPending())
+ return net::LOAD_STATE_READING_RESPONSE;
+ return net::LOAD_STATE_IDLE;
+}
+
+bool AppCacheURLRequestJob::GetMimeType(std::string* mime_type) const {
+ if (!http_info())
+ return false;
+ return http_info()->headers->GetMimeType(mime_type);
+}
+
+bool AppCacheURLRequestJob::GetCharset(std::string* charset) {
+ if (!http_info())
+ return false;
+ return http_info()->headers->GetCharset(charset);
+}
+
+void AppCacheURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
+ if (!http_info())
+ return;
+ *info = *http_info();
+}
+
+int AppCacheURLRequestJob::GetResponseCode() const {
+ if (!http_info())
+ return -1;
+ return http_info()->headers->response_code();
+}
+
+bool AppCacheURLRequestJob::ReadRawData(net::IOBuffer* buf, int buf_size,
+ int *bytes_read) {
+ DCHECK(is_delivering_appcache_response());
+ DCHECK_NE(buf_size, 0);
+ DCHECK(bytes_read);
+ DCHECK(!reader_->IsReadPending());
+ reader_->ReadData(
+ buf, buf_size, base::Bind(&AppCacheURLRequestJob::OnReadComplete,
+ base::Unretained(this)));
+ SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
+ return false;
+}
+
+void AppCacheURLRequestJob::SetExtraRequestHeaders(
+ const net::HttpRequestHeaders& headers) {
+ std::string value;
+ std::vector<net::HttpByteRange> ranges;
+ if (!headers.GetHeader(net::HttpRequestHeaders::kRange, &value) ||
+ !net::HttpUtil::ParseRangeHeader(value, &ranges)) {
+ return;
+ }
+
+ // If multiple ranges are requested, we play dumb and
+ // return the entire response with 200 OK.
+ if (ranges.size() == 1U)
+ range_requested_ = ranges[0];
+}
+
+} // namespace content
diff --git a/content/browser/appcache/appcache_url_request_job.h b/content/browser/appcache/appcache_url_request_job.h
new file mode 100644
index 0000000..4b9bbcd
--- /dev/null
+++ b/content/browser/appcache/appcache_url_request_job.h
@@ -0,0 +1,182 @@
+// Copyright (c) 2012 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.
+
+#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_URL_REQUEST_JOB_H_
+#define CONTENT_BROWSER_APPCACHE_APPCACHE_URL_REQUEST_JOB_H_
+
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "content/browser/appcache/appcache_entry.h"
+#include "content/browser/appcache/appcache_executable_handler.h"
+#include "content/browser/appcache/appcache_response.h"
+#include "content/browser/appcache/appcache_storage.h"
+#include "content/common/content_export.h"
+#include "net/http/http_byte_range.h"
+#include "net/url_request/url_request_job.h"
+
+namespace content {
+class AppCacheRequestHandlerTest;
+class AppCacheURLRequestJobTest;
+}
+
+namespace net {
+class GrowableIOBuffer;
+};
+
+namespace content {
+
+class AppCacheHost;
+
+// A net::URLRequestJob derivative that knows how to return a response stored
+// in the appcache.
+class CONTENT_EXPORT AppCacheURLRequestJob
+ : public net::URLRequestJob,
+ public AppCacheStorage::Delegate {
+ public:
+ AppCacheURLRequestJob(net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ AppCacheStorage* storage,
+ AppCacheHost* host,
+ bool is_main_resource);
+
+ // Informs the job of what response it should deliver. Only one of these
+ // methods should be called, and only once per job. A job will sit idle and
+ // wait indefinitely until one of the deliver methods is called.
+ void DeliverAppCachedResponse(const GURL& manifest_url, int64 group_id,
+ int64 cache_id, const AppCacheEntry& entry,
+ bool is_fallback);
+ void DeliverNetworkResponse();
+ void DeliverErrorResponse();
+
+ bool is_waiting() const {
+ return delivery_type_ == AWAITING_DELIVERY_ORDERS;
+ }
+
+ bool is_delivering_appcache_response() const {
+ return delivery_type_ == APPCACHED_DELIVERY;
+ }
+
+ bool is_delivering_network_response() const {
+ return delivery_type_ == NETWORK_DELIVERY;
+ }
+
+ bool is_delivering_error_response() const {
+ return delivery_type_ == ERROR_DELIVERY;
+ }
+
+ // Accessors for the info about the appcached response, if any,
+ // that this job has been instructed to deliver. These are only
+ // valid to call if is_delivering_appcache_response.
+ const GURL& manifest_url() const { return manifest_url_; }
+ int64 group_id() const { return group_id_; }
+ int64 cache_id() const { return cache_id_; }
+ const AppCacheEntry& entry() const { return entry_; }
+
+ // net::URLRequestJob's Kill method is made public so the users of this
+ // class in the appcache namespace can call it.
+ virtual void Kill() OVERRIDE;
+
+ // Returns true if the job has been started by the net library.
+ bool has_been_started() const {
+ return has_been_started_;
+ }
+
+ // Returns true if the job has been killed.
+ bool has_been_killed() const {
+ return has_been_killed_;
+ }
+
+ // Returns true if the cache entry was not found in the disk cache.
+ bool cache_entry_not_found() const {
+ return cache_entry_not_found_;
+ }
+
+ protected:
+ virtual ~AppCacheURLRequestJob();
+
+ private:
+ friend class content::AppCacheRequestHandlerTest;
+ friend class content::AppCacheURLRequestJobTest;
+
+ enum DeliveryType {
+ AWAITING_DELIVERY_ORDERS,
+ APPCACHED_DELIVERY,
+ NETWORK_DELIVERY,
+ ERROR_DELIVERY
+ };
+
+ // Returns true if one of the Deliver methods has been called.
+ bool has_delivery_orders() const {
+ return !is_waiting();
+ }
+
+ void MaybeBeginDelivery();
+ void BeginDelivery();
+
+ // For executable response handling.
+ void BeginExecutableHandlerDelivery();
+ void OnExecutableSourceLoaded(int result);
+ void InvokeExecutableHandler(AppCacheExecutableHandler* handler);
+ void OnExecutableResponseCallback(
+ const AppCacheExecutableHandler::Response& response);
+ void BeginErrorDelivery(const char* message);
+
+ // AppCacheStorage::Delegate methods
+ virtual void OnResponseInfoLoaded(
+ AppCacheResponseInfo* response_info, int64 response_id) OVERRIDE;
+ virtual void OnCacheLoaded(AppCache* cache, int64 cache_id) OVERRIDE;
+
+ const net::HttpResponseInfo* http_info() const;
+ bool is_range_request() const { return range_requested_.IsValid(); }
+ void SetupRangeResponse();
+
+ // AppCacheResponseReader completion callback
+ void OnReadComplete(int result);
+
+ // net::URLRequestJob methods, see url_request_job.h for doc comments
+ virtual void Start() OVERRIDE;
+ virtual net::LoadState GetLoadState() const OVERRIDE;
+ virtual bool GetCharset(std::string* charset) OVERRIDE;
+ virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE;
+ virtual bool ReadRawData(net::IOBuffer* buf,
+ int buf_size,
+ int *bytes_read) OVERRIDE;
+
+ // Sets extra request headers for Job types that support request headers.
+ // This is how we get informed of range-requests.
+ virtual void SetExtraRequestHeaders(
+ const net::HttpRequestHeaders& headers) OVERRIDE;
+
+ // FilterContext methods
+ virtual bool GetMimeType(std::string* mime_type) const OVERRIDE;
+ virtual int GetResponseCode() const OVERRIDE;
+
+ AppCacheHost* host_;
+ AppCacheStorage* storage_;
+ base::TimeTicks start_time_tick_;
+ bool has_been_started_;
+ bool has_been_killed_;
+ DeliveryType delivery_type_;
+ GURL manifest_url_;
+ int64 group_id_;
+ int64 cache_id_;
+ AppCacheEntry entry_;
+ bool is_fallback_;
+ bool is_main_resource_; // Used for histogram logging.
+ bool cache_entry_not_found_;
+ scoped_refptr<AppCacheResponseInfo> info_;
+ scoped_refptr<net::GrowableIOBuffer> handler_source_buffer_;
+ scoped_ptr<AppCacheResponseReader> handler_source_reader_;
+ net::HttpByteRange range_requested_;
+ scoped_ptr<net::HttpResponseInfo> range_response_info_;
+ scoped_ptr<AppCacheResponseReader> reader_;
+ scoped_refptr<AppCache> cache_;
+ scoped_refptr<AppCacheGroup> group_;
+ base::WeakPtrFactory<AppCacheURLRequestJob> weak_factory_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_APPCACHE_APPCACHE_REQUEST_HANDLER_H_
diff --git a/content/browser/appcache/appcache_url_request_job_unittest.cc b/content/browser/appcache/appcache_url_request_job_unittest.cc
index edaa07e..7c70eed 100644
--- a/content/browser/appcache/appcache_url_request_job_unittest.cc
+++ b/content/browser/appcache/appcache_url_request_job_unittest.cc
@@ -12,6 +12,8 @@
#include "base/pickle.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
+#include "content/browser/appcache/appcache_response.h"
+#include "content/browser/appcache/appcache_url_request_job.h"
#include "content/browser/appcache/mock_appcache_service.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
@@ -22,17 +24,7 @@
#include "net/url_request/url_request_error_job.h"
#include "net/url_request/url_request_job_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/appcache/appcache_response.h"
-#include "webkit/browser/appcache/appcache_url_request_job.h"
-
-using appcache::AppCacheEntry;
-using appcache::AppCacheStorage;
-using appcache::AppCacheResponseInfo;
-using appcache::AppCacheResponseReader;
-using appcache::AppCacheResponseWriter;
-using appcache::AppCacheURLRequestJob;
-using appcache::HttpResponseInfoIOBuffer;
-using appcache::kAppCacheNoCacheId;
+
using net::IOBuffer;
using net::WrappedIOBuffer;
diff --git a/content/browser/appcache/appcache_working_set.cc b/content/browser/appcache/appcache_working_set.cc
new file mode 100644
index 0000000..4394097
--- /dev/null
+++ b/content/browser/appcache/appcache_working_set.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2009 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 "content/browser/appcache/appcache_working_set.h"
+
+#include "base/logging.h"
+#include "content/browser/appcache/appcache.h"
+#include "content/browser/appcache/appcache_group.h"
+#include "content/browser/appcache/appcache_response.h"
+
+namespace content {
+
+AppCacheWorkingSet::AppCacheWorkingSet() : is_disabled_(false) {}
+
+AppCacheWorkingSet::~AppCacheWorkingSet() {
+ DCHECK(caches_.empty());
+ DCHECK(groups_.empty());
+ DCHECK(groups_by_origin_.empty());
+}
+
+void AppCacheWorkingSet::Disable() {
+ if (is_disabled_)
+ return;
+ is_disabled_ = true;
+ caches_.clear();
+ groups_.clear();
+ groups_by_origin_.clear();
+ response_infos_.clear();
+}
+
+void AppCacheWorkingSet::AddCache(AppCache* cache) {
+ if (is_disabled_)
+ return;
+ DCHECK(cache->cache_id() != kAppCacheNoCacheId);
+ int64 cache_id = cache->cache_id();
+ DCHECK(caches_.find(cache_id) == caches_.end());
+ caches_.insert(CacheMap::value_type(cache_id, cache));
+}
+
+void AppCacheWorkingSet::RemoveCache(AppCache* cache) {
+ caches_.erase(cache->cache_id());
+}
+
+void AppCacheWorkingSet::AddGroup(AppCacheGroup* group) {
+ if (is_disabled_)
+ return;
+ const GURL& url = group->manifest_url();
+ DCHECK(groups_.find(url) == groups_.end());
+ groups_.insert(GroupMap::value_type(url, group));
+ groups_by_origin_[url.GetOrigin()].insert(GroupMap::value_type(url, group));
+}
+
+void AppCacheWorkingSet::RemoveGroup(AppCacheGroup* group) {
+ const GURL& url = group->manifest_url();
+ groups_.erase(url);
+
+ GURL origin_url = url.GetOrigin();
+ GroupMap* groups_in_origin = GetMutableGroupsInOrigin(origin_url);
+ if (groups_in_origin) {
+ groups_in_origin->erase(url);
+ if (groups_in_origin->empty())
+ groups_by_origin_.erase(origin_url);
+ }
+}
+
+void AppCacheWorkingSet::AddResponseInfo(AppCacheResponseInfo* info) {
+ if (is_disabled_)
+ return;
+ DCHECK(info->response_id() != kAppCacheNoResponseId);
+ int64 response_id = info->response_id();
+ DCHECK(response_infos_.find(response_id) == response_infos_.end());
+ response_infos_.insert(ResponseInfoMap::value_type(response_id, info));
+}
+
+void AppCacheWorkingSet::RemoveResponseInfo(AppCacheResponseInfo* info) {
+ response_infos_.erase(info->response_id());
+}
+
+} // namespace
diff --git a/content/browser/appcache/appcache_working_set.h b/content/browser/appcache/appcache_working_set.h
new file mode 100644
index 0000000..5b9d00a
--- /dev/null
+++ b/content/browser/appcache/appcache_working_set.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2011 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.
+
+#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_WORKING_SET_H_
+#define CONTENT_BROWSER_APPCACHE_APPCACHE_WORKING_SET_H_
+
+#include <map>
+
+#include "base/containers/hash_tables.h"
+#include "content/common/content_export.h"
+#include "url/gurl.h"
+
+namespace content {
+
+class AppCache;
+class AppCacheGroup;
+class AppCacheResponseInfo;
+
+// Represents the working set of appcache object instances
+// currently in memory.
+class CONTENT_EXPORT AppCacheWorkingSet {
+ public:
+ typedef std::map<GURL, AppCacheGroup*> GroupMap;
+
+ AppCacheWorkingSet();
+ ~AppCacheWorkingSet();
+
+ void Disable();
+ bool is_disabled() const { return is_disabled_; }
+
+ void AddCache(AppCache* cache);
+ void RemoveCache(AppCache* cache);
+ AppCache* GetCache(int64 id) {
+ CacheMap::iterator it = caches_.find(id);
+ return (it != caches_.end()) ? it->second : NULL;
+ }
+
+ void AddGroup(AppCacheGroup* group);
+ void RemoveGroup(AppCacheGroup* group);
+ AppCacheGroup* GetGroup(const GURL& manifest_url) {
+ GroupMap::iterator it = groups_.find(manifest_url);
+ return (it != groups_.end()) ? it->second : NULL;
+ }
+
+ const GroupMap* GetGroupsInOrigin(const GURL& origin_url) {
+ return GetMutableGroupsInOrigin(origin_url);
+ }
+
+ void AddResponseInfo(AppCacheResponseInfo* response_info);
+ void RemoveResponseInfo(AppCacheResponseInfo* response_info);
+ AppCacheResponseInfo* GetResponseInfo(int64 id) {
+ ResponseInfoMap::iterator it = response_infos_.find(id);
+ return (it != response_infos_.end()) ? it->second : NULL;
+ }
+
+ private:
+ typedef base::hash_map<int64, AppCache*> CacheMap;
+ typedef std::map<GURL, GroupMap> GroupsByOriginMap;
+ typedef base::hash_map<int64, AppCacheResponseInfo*> ResponseInfoMap;
+
+ GroupMap* GetMutableGroupsInOrigin(const GURL& origin_url) {
+ GroupsByOriginMap::iterator it = groups_by_origin_.find(origin_url);
+ return (it != groups_by_origin_.end()) ? &it->second : NULL;
+ }
+
+ CacheMap caches_;
+ GroupMap groups_;
+ GroupsByOriginMap groups_by_origin_; // origin -> (manifest -> group)
+ ResponseInfoMap response_infos_;
+ bool is_disabled_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_APPCACHE_APPCACHE_WORKING_SET_H_
diff --git a/content/browser/appcache/chrome_appcache_service.cc b/content/browser/appcache/chrome_appcache_service.cc
index c72c218..8d686e8 100644
--- a/content/browser/appcache/chrome_appcache_service.cc
+++ b/content/browser/appcache/chrome_appcache_service.cc
@@ -5,12 +5,12 @@
#include "content/browser/appcache/chrome_appcache_service.h"
#include "base/files/file_path.h"
+#include "content/browser/appcache/appcache_storage_impl.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/resource_context.h"
#include "net/base/net_errors.h"
#include "net/url_request/url_request_context_getter.h"
-#include "webkit/browser/appcache/appcache_storage_impl.h"
#include "webkit/browser/quota/quota_manager.h"
namespace content {
diff --git a/content/browser/appcache/chrome_appcache_service.h b/content/browser/appcache/chrome_appcache_service.h
index 2f33d9b..8bc54d4 100644
--- a/content/browser/appcache/chrome_appcache_service.h
+++ b/content/browser/appcache/chrome_appcache_service.h
@@ -8,9 +8,9 @@
#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
#include "base/sequenced_task_runner_helpers.h"
+#include "content/browser/appcache/appcache_policy.h"
+#include "content/browser/appcache/appcache_service_impl.h"
#include "content/common/content_export.h"
-#include "webkit/browser/appcache/appcache_policy.h"
-#include "webkit/browser/appcache/appcache_service_impl.h"
#include "webkit/browser/quota/special_storage_policy.h"
namespace base {
@@ -40,8 +40,8 @@ struct ChromeAppCacheServiceDeleter;
class CONTENT_EXPORT ChromeAppCacheService
: public base::RefCountedThreadSafe<ChromeAppCacheService,
ChromeAppCacheServiceDeleter>,
- NON_EXPORTED_BASE(public appcache::AppCacheServiceImpl),
- NON_EXPORTED_BASE(public appcache::AppCachePolicy) {
+ NON_EXPORTED_BASE(public AppCacheServiceImpl),
+ NON_EXPORTED_BASE(public AppCachePolicy) {
public:
explicit ChromeAppCacheService(quota::QuotaManagerProxy* proxy);
diff --git a/content/browser/appcache/chrome_appcache_service_unittest.cc b/content/browser/appcache/chrome_appcache_service_unittest.cc
index 1800d0b..98bf68a 100644
--- a/content/browser/appcache/chrome_appcache_service_unittest.cc
+++ b/content/browser/appcache/chrome_appcache_service_unittest.cc
@@ -7,6 +7,8 @@
#include "base/files/scoped_temp_dir.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
+#include "content/browser/appcache/appcache_database.h"
+#include "content/browser/appcache/appcache_storage_impl.h"
#include "content/browser/appcache/chrome_appcache_service.h"
#include "content/browser/browser_thread_impl.h"
#include "content/public/browser/resource_context.h"
@@ -15,8 +17,6 @@
#include "content/test/appcache_test_helper.h"
#include "net/url_request/url_request_context_getter.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/appcache/appcache_database.h"
-#include "webkit/browser/appcache/appcache_storage_impl.h"
#include <set>
@@ -116,8 +116,8 @@ ChromeAppCacheServiceTest::CreateAppCacheServiceImpl(
// Steps needed to initialize the storage of AppCache data.
message_loop_.RunUntilIdle();
if (init_storage) {
- appcache::AppCacheStorageImpl* storage =
- static_cast<appcache::AppCacheStorageImpl*>(
+ AppCacheStorageImpl* storage =
+ static_cast<AppCacheStorageImpl*>(
appcache_service->storage());
storage->database_->db_connection();
storage->disk_cache();
diff --git a/content/browser/appcache/manifest_parser.cc b/content/browser/appcache/manifest_parser.cc
new file mode 100644
index 0000000..fb63f22
--- /dev/null
+++ b/content/browser/appcache/manifest_parser.cc
@@ -0,0 +1,382 @@
+// Copyright (c) 2011 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.
+//
+// This is a port of ManifestParser.cc from WebKit/WebCore/loader/appcache.
+
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "content/browser/appcache/manifest_parser.h"
+
+#include "base/command_line.h"
+#include "base/i18n/icu_string_conversions.h"
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "url/gurl.h"
+
+namespace content {
+
+namespace {
+
+// Helper function used to identify 'isPattern' annotations.
+bool HasPatternMatchingAnnotation(const wchar_t* line_p,
+ const wchar_t* line_end) {
+ // Skip whitespace separating the resource url from the annotation.
+ // Note: trailing whitespace has already been trimmed from the line.
+ while (line_p < line_end && (*line_p == '\t' || *line_p == ' '))
+ ++line_p;
+ if (line_p == line_end)
+ return false;
+ std::wstring annotation(line_p, line_end - line_p);
+ return annotation == L"isPattern";
+}
+
+}
+
+enum Mode {
+ EXPLICIT,
+ INTERCEPT,
+ FALLBACK,
+ ONLINE_WHITELIST,
+ UNKNOWN_MODE,
+};
+
+enum InterceptVerb {
+ RETURN,
+ EXECUTE,
+ UNKNOWN_VERB,
+};
+
+Manifest::Manifest()
+ : online_whitelist_all(false),
+ did_ignore_intercept_namespaces(false) {
+}
+
+Manifest::~Manifest() {}
+
+bool ParseManifest(const GURL& manifest_url, const char* data, int length,
+ ParseMode parse_mode, Manifest& manifest) {
+ // This is an implementation of the parsing algorithm specified in
+ // the HTML5 offline web application docs:
+ // http://www.w3.org/TR/html5/offline.html
+ // Do not modify it without consulting those docs.
+ // Though you might be tempted to convert these wstrings to UTF-8 or
+ // base::string16, this implementation seems simpler given the constraints.
+
+ const wchar_t kSignature[] = L"CACHE MANIFEST";
+ const size_t kSignatureLength = arraysize(kSignature) - 1;
+ const wchar_t kChromiumSignature[] = L"CHROMIUM CACHE MANIFEST";
+ const size_t kChromiumSignatureLength = arraysize(kChromiumSignature) - 1;
+
+ DCHECK(manifest.explicit_urls.empty());
+ DCHECK(manifest.fallback_namespaces.empty());
+ DCHECK(manifest.online_whitelist_namespaces.empty());
+ DCHECK(!manifest.online_whitelist_all);
+ DCHECK(!manifest.did_ignore_intercept_namespaces);
+
+ Mode mode = EXPLICIT;
+
+ std::wstring data_string;
+ // TODO(jennb): cannot do UTF8ToWide(data, length, &data_string);
+ // until UTF8ToWide uses 0xFFFD Unicode replacement character.
+ base::CodepageToWide(std::string(data, length), base::kCodepageUTF8,
+ base::OnStringConversionError::SUBSTITUTE, &data_string);
+ const wchar_t* p = data_string.c_str();
+ const wchar_t* end = p + data_string.length();
+
+ // Look for the magic signature: "^\xFEFF?CACHE MANIFEST[ \t]?"
+ // Example: "CACHE MANIFEST #comment" is a valid signature.
+ // Example: "CACHE MANIFEST;V2" is not.
+
+ // When the input data starts with a UTF-8 Byte-Order-Mark
+ // (0xEF, 0xBB, 0xBF), the UTF8ToWide() function converts it to a
+ // Unicode BOM (U+FEFF). Skip a converted Unicode BOM if it exists.
+ int bom_offset = 0;
+ if (!data_string.empty() && data_string[0] == 0xFEFF) {
+ bom_offset = 1;
+ ++p;
+ }
+
+ if (p >= end)
+ return false;
+
+ // Check for a supported signature and skip p past it.
+ if (0 == data_string.compare(bom_offset, kSignatureLength,
+ kSignature)) {
+ p += kSignatureLength;
+ } else if (0 == data_string.compare(bom_offset, kChromiumSignatureLength,
+ kChromiumSignature)) {
+ p += kChromiumSignatureLength;
+ } else {
+ return false;
+ }
+
+ // Character after "CACHE MANIFEST" must be whitespace.
+ if (p < end && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r')
+ return false;
+
+ // Skip to the end of the line.
+ while (p < end && *p != '\r' && *p != '\n')
+ ++p;
+
+ while (1) {
+ // Skip whitespace
+ while (p < end && (*p == '\n' || *p == '\r' || *p == ' ' || *p == '\t'))
+ ++p;
+
+ if (p == end)
+ break;
+
+ const wchar_t* line_start = p;
+
+ // Find the end of the line
+ while (p < end && *p != '\r' && *p != '\n')
+ ++p;
+
+ // Check if we have a comment
+ if (*line_start == '#')
+ continue;
+
+ // Get rid of trailing whitespace
+ const wchar_t* tmp = p - 1;
+ while (tmp > line_start && (*tmp == ' ' || *tmp == '\t'))
+ --tmp;
+
+ std::wstring line(line_start, tmp - line_start + 1);
+
+ if (line == L"CACHE:") {
+ mode = EXPLICIT;
+ } else if (line == L"FALLBACK:") {
+ mode = FALLBACK;
+ } else if (line == L"NETWORK:") {
+ mode = ONLINE_WHITELIST;
+ } else if (line == L"CHROMIUM-INTERCEPT:") {
+ mode = INTERCEPT;
+ } else if (*(line.end() - 1) == ':') {
+ mode = UNKNOWN_MODE;
+ } else if (mode == UNKNOWN_MODE) {
+ continue;
+ } else if (line == L"*" && mode == ONLINE_WHITELIST) {
+ manifest.online_whitelist_all = true;
+ continue;
+ } else if (mode == EXPLICIT || mode == ONLINE_WHITELIST) {
+ const wchar_t *line_p = line.c_str();
+ const wchar_t *line_end = line_p + line.length();
+
+ // Look for whitespace separating the URL from subsequent ignored tokens.
+ while (line_p < line_end && *line_p != '\t' && *line_p != ' ')
+ ++line_p;
+
+ base::string16 url16;
+ base::WideToUTF16(line.c_str(), line_p - line.c_str(), &url16);
+ GURL url = manifest_url.Resolve(url16);
+ if (!url.is_valid())
+ continue;
+ if (url.has_ref()) {
+ GURL::Replacements replacements;
+ replacements.ClearRef();
+ url = url.ReplaceComponents(replacements);
+ }
+
+ // Scheme component must be the same as the manifest URL's.
+ if (url.scheme() != manifest_url.scheme()) {
+ continue;
+ }
+
+ // See http://code.google.com/p/chromium/issues/detail?id=69594
+ // We willfully violate the HTML5 spec at this point in order
+ // to support the appcaching of cross-origin HTTPS resources.
+ // Per the spec, EXPLICIT cross-origin HTTS resources should be
+ // ignored here. We've opted for a milder constraint and allow
+ // caching unless the resource has a "no-store" header. That
+ // condition is enforced in AppCacheUpdateJob.
+
+ if (mode == EXPLICIT) {
+ manifest.explicit_urls.insert(url.spec());
+ } else {
+ bool is_pattern = HasPatternMatchingAnnotation(line_p, line_end);
+ manifest.online_whitelist_namespaces.push_back(
+ AppCacheNamespace(APPCACHE_NETWORK_NAMESPACE, url, GURL(),
+ is_pattern));
+ }
+ } else if (mode == INTERCEPT) {
+ if (parse_mode != PARSE_MANIFEST_ALLOWING_INTERCEPTS) {
+ manifest.did_ignore_intercept_namespaces = true;
+ continue;
+ }
+
+ // Lines of the form,
+ // <urlnamespace> <intercept_type> <targeturl>
+ const wchar_t* line_p = line.c_str();
+ const wchar_t* line_end = line_p + line.length();
+
+ // Look for first whitespace separating the url namespace from
+ // the intercept type.
+ while (line_p < line_end && *line_p != '\t' && *line_p != ' ')
+ ++line_p;
+
+ if (line_p == line_end)
+ continue; // There was no whitespace separating the URLs.
+
+ base::string16 namespace_url16;
+ base::WideToUTF16(line.c_str(), line_p - line.c_str(), &namespace_url16);
+ GURL namespace_url = manifest_url.Resolve(namespace_url16);
+ if (!namespace_url.is_valid())
+ continue;
+ if (namespace_url.has_ref()) {
+ GURL::Replacements replacements;
+ replacements.ClearRef();
+ namespace_url = namespace_url.ReplaceComponents(replacements);
+ }
+
+ // The namespace URL must have the same scheme, host and port
+ // as the manifest's URL.
+ if (manifest_url.GetOrigin() != namespace_url.GetOrigin())
+ continue;
+
+ // Skip whitespace separating namespace from the type.
+ while (line_p < line_end && (*line_p == '\t' || *line_p == ' '))
+ ++line_p;
+
+ // Look for whitespace separating the type from the target url.
+ const wchar_t* type_start = line_p;
+ while (line_p < line_end && *line_p != '\t' && *line_p != ' ')
+ ++line_p;
+
+ // Look for a type value we understand, otherwise skip the line.
+ InterceptVerb verb = UNKNOWN_VERB;
+ std::wstring type(type_start, line_p - type_start);
+ if (type == L"return") {
+ verb = RETURN;
+ } else if (type == L"execute" &&
+ CommandLine::ForCurrentProcess()->HasSwitch(
+ kEnableExecutableHandlers)) {
+ verb = EXECUTE;
+ }
+ if (verb == UNKNOWN_VERB)
+ continue;
+
+ // Skip whitespace separating type from the target_url.
+ while (line_p < line_end && (*line_p == '\t' || *line_p == ' '))
+ ++line_p;
+
+ // Look for whitespace separating the URL from subsequent ignored tokens.
+ const wchar_t* target_url_start = line_p;
+ while (line_p < line_end && *line_p != '\t' && *line_p != ' ')
+ ++line_p;
+
+ base::string16 target_url16;
+ base::WideToUTF16(target_url_start, line_p - target_url_start,
+ &target_url16);
+ GURL target_url = manifest_url.Resolve(target_url16);
+ if (!target_url.is_valid())
+ continue;
+
+ if (target_url.has_ref()) {
+ GURL::Replacements replacements;
+ replacements.ClearRef();
+ target_url = target_url.ReplaceComponents(replacements);
+ }
+ if (manifest_url.GetOrigin() != target_url.GetOrigin())
+ continue;
+
+ bool is_pattern = HasPatternMatchingAnnotation(line_p, line_end);
+ manifest.intercept_namespaces.push_back(
+ AppCacheNamespace(APPCACHE_INTERCEPT_NAMESPACE, namespace_url,
+ target_url, is_pattern, verb == EXECUTE));
+ } else if (mode == FALLBACK) {
+ const wchar_t* line_p = line.c_str();
+ const wchar_t* line_end = line_p + line.length();
+
+ // Look for whitespace separating the two URLs
+ while (line_p < line_end && *line_p != '\t' && *line_p != ' ')
+ ++line_p;
+
+ if (line_p == line_end) {
+ // There was no whitespace separating the URLs.
+ continue;
+ }
+
+ base::string16 namespace_url16;
+ base::WideToUTF16(line.c_str(), line_p - line.c_str(), &namespace_url16);
+ GURL namespace_url = manifest_url.Resolve(namespace_url16);
+ if (!namespace_url.is_valid())
+ continue;
+ if (namespace_url.has_ref()) {
+ GURL::Replacements replacements;
+ replacements.ClearRef();
+ namespace_url = namespace_url.ReplaceComponents(replacements);
+ }
+
+ // Fallback namespace URL must have the same scheme, host and port
+ // as the manifest's URL.
+ if (manifest_url.GetOrigin() != namespace_url.GetOrigin()) {
+ continue;
+ }
+
+ // Skip whitespace separating fallback namespace from URL.
+ while (line_p < line_end && (*line_p == '\t' || *line_p == ' '))
+ ++line_p;
+
+ // Look for whitespace separating the URL from subsequent ignored tokens.
+ const wchar_t* fallback_start = line_p;
+ while (line_p < line_end && *line_p != '\t' && *line_p != ' ')
+ ++line_p;
+
+ base::string16 fallback_url16;
+ base::WideToUTF16(fallback_start, line_p - fallback_start,
+ &fallback_url16);
+ GURL fallback_url = manifest_url.Resolve(fallback_url16);
+ if (!fallback_url.is_valid())
+ continue;
+ if (fallback_url.has_ref()) {
+ GURL::Replacements replacements;
+ replacements.ClearRef();
+ fallback_url = fallback_url.ReplaceComponents(replacements);
+ }
+
+ // Fallback entry URL must have the same scheme, host and port
+ // as the manifest's URL.
+ if (manifest_url.GetOrigin() != fallback_url.GetOrigin()) {
+ continue;
+ }
+
+ bool is_pattern = HasPatternMatchingAnnotation(line_p, line_end);
+
+ // Store regardless of duplicate namespace URL. Only first match
+ // will ever be used.
+ manifest.fallback_namespaces.push_back(
+ AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE, namespace_url,
+ fallback_url, is_pattern));
+ } else {
+ NOTREACHED();
+ }
+ }
+
+ return true;
+}
+
+} // namespace content
diff --git a/content/browser/appcache/manifest_parser.h b/content/browser/appcache/manifest_parser.h
new file mode 100644
index 0000000..4deb599
--- /dev/null
+++ b/content/browser/appcache/manifest_parser.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2011 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.
+//
+// This is a port of ManifestParser.h from WebKit/WebCore/loader/appcache.
+
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CONTENT_BROWSER_APPCACHE_MANIFEST_PARSER_H_
+#define CONTENT_BROWSER_APPCACHE_MANIFEST_PARSER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "content/common/appcache_interfaces.h"
+#include "content/common/content_export.h"
+
+class GURL;
+
+namespace content {
+
+struct CONTENT_EXPORT Manifest {
+ Manifest();
+ ~Manifest();
+
+ base::hash_set<std::string> explicit_urls;
+ AppCacheNamespaceVector intercept_namespaces;
+ AppCacheNamespaceVector fallback_namespaces;
+ AppCacheNamespaceVector online_whitelist_namespaces;
+ bool online_whitelist_all;
+ bool did_ignore_intercept_namespaces;
+};
+
+enum ParseMode {
+ PARSE_MANIFEST_PER_STANDARD,
+ PARSE_MANIFEST_ALLOWING_INTERCEPTS
+};
+
+CONTENT_EXPORT bool ParseManifest(
+ const GURL& manifest_url,
+ const char* data,
+ int length,
+ ParseMode parse_mode,
+ Manifest& manifest);
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_APPCACHE_MANIFEST_PARSER_H_
diff --git a/content/browser/appcache/manifest_parser_unittest.cc b/content/browser/appcache/manifest_parser_unittest.cc
index 8a44376..caefca37 100644
--- a/content/browser/appcache/manifest_parser_unittest.cc
+++ b/content/browser/appcache/manifest_parser_unittest.cc
@@ -4,17 +4,9 @@
#include <string>
+#include "content/browser/appcache/manifest_parser.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
-#include "webkit/browser/appcache/manifest_parser.h"
-
-using appcache::Manifest;
-using appcache::NamespaceVector;
-using appcache::APPCACHE_FALLBACK_NAMESPACE;
-using appcache::APPCACHE_INTERCEPT_NAMESPACE;
-using appcache::APPCACHE_NETWORK_NAMESPACE;
-using appcache::PARSE_MANIFEST_ALLOWING_INTERCEPTS;
-using appcache::PARSE_MANIFEST_PER_STANDARD;
namespace content {
@@ -167,7 +159,7 @@ TEST(AppCacheManifestParserTest, WhitelistUrls) {
EXPECT_TRUE(manifest.intercept_namespaces.empty());
EXPECT_FALSE(manifest.online_whitelist_all);
- const NamespaceVector& online = manifest.online_whitelist_namespaces;
+ const AppCacheNamespaceVector& online = manifest.online_whitelist_namespaces;
const size_t kExpected = 6;
ASSERT_EQ(kExpected, online.size());
EXPECT_EQ(APPCACHE_NETWORK_NAMESPACE, online[0].type);
@@ -213,7 +205,7 @@ TEST(AppCacheManifestParserTest, FallbackUrls) {
EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
EXPECT_FALSE(manifest.online_whitelist_all);
- const NamespaceVector& fallbacks = manifest.fallback_namespaces;
+ const AppCacheNamespaceVector& fallbacks = manifest.fallback_namespaces;
const size_t kExpected = 5;
ASSERT_EQ(kExpected, fallbacks.size());
EXPECT_EQ(APPCACHE_FALLBACK_NAMESPACE, fallbacks[0].type);
@@ -264,7 +256,7 @@ TEST(AppCacheManifestParserTest, FallbackUrlsWithPort) {
EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
EXPECT_FALSE(manifest.online_whitelist_all);
- const NamespaceVector& fallbacks = manifest.fallback_namespaces;
+ const AppCacheNamespaceVector& fallbacks = manifest.fallback_namespaces;
const size_t kExpected = 3;
ASSERT_EQ(kExpected, fallbacks.size());
EXPECT_EQ(APPCACHE_FALLBACK_NAMESPACE, fallbacks[0].type);
@@ -308,7 +300,7 @@ TEST(AppCacheManifestParserTest, InterceptUrls) {
EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
EXPECT_FALSE(manifest.online_whitelist_all);
- const NamespaceVector& intercepts = manifest.intercept_namespaces;
+ const AppCacheNamespaceVector& intercepts = manifest.intercept_namespaces;
const size_t kExpected = 3;
ASSERT_EQ(kExpected, intercepts.size());
EXPECT_EQ(APPCACHE_INTERCEPT_NAMESPACE, intercepts[0].type);
@@ -372,7 +364,7 @@ TEST(AppCacheManifestParserTest, ComboUrls) {
EXPECT_TRUE(urls.find("http://combo.com:99/explicit-2") != urls.end());
EXPECT_TRUE(urls.find("http://www.diff.com/explicit-3") != urls.end());
- const NamespaceVector& online = manifest.online_whitelist_namespaces;
+ const AppCacheNamespaceVector& online = manifest.online_whitelist_namespaces;
expected = 4;
ASSERT_EQ(expected, online.size());
EXPECT_EQ(GURL("http://combo.com/whitelist-1"),
@@ -384,7 +376,7 @@ TEST(AppCacheManifestParserTest, ComboUrls) {
EXPECT_EQ(GURL("http://combo.com:99/whitelist-4"),
online[3].namespace_url);
- const NamespaceVector& fallbacks = manifest.fallback_namespaces;
+ const AppCacheNamespaceVector& fallbacks = manifest.fallback_namespaces;
expected = 2;
ASSERT_EQ(expected, fallbacks.size());
EXPECT_EQ(APPCACHE_FALLBACK_NAMESPACE, fallbacks[0].type);
diff --git a/content/browser/appcache/mock_appcache_policy.h b/content/browser/appcache/mock_appcache_policy.h
index bc75764..93de476 100644
--- a/content/browser/appcache/mock_appcache_policy.h
+++ b/content/browser/appcache/mock_appcache_policy.h
@@ -6,12 +6,12 @@
#define CONTENT_BROWSER_APPCACHE_MOCK_APPCACHE_POLICY_H_
#include "base/compiler_specific.h"
+#include "content/browser/appcache/appcache_policy.h"
#include "url/gurl.h"
-#include "webkit/browser/appcache/appcache_policy.h"
namespace content {
-class MockAppCachePolicy : public appcache::AppCachePolicy {
+class MockAppCachePolicy : public AppCachePolicy {
public:
MockAppCachePolicy();
virtual ~MockAppCachePolicy();
diff --git a/content/browser/appcache/mock_appcache_service.h b/content/browser/appcache/mock_appcache_service.h
index 691736b..ad86c54 100644
--- a/content/browser/appcache/mock_appcache_service.h
+++ b/content/browser/appcache/mock_appcache_service.h
@@ -6,12 +6,10 @@
#define CONTENT_BROWSER_APPCACHE_MOCK_APPCACHE_SERVICE_H_
#include "base/compiler_specific.h"
+#include "content/browser/appcache/appcache_service_impl.h"
#include "content/browser/appcache/mock_appcache_storage.h"
-#include "webkit/browser/appcache/appcache_service_impl.h"
#include "webkit/browser/quota/quota_manager.h"
-using appcache::AppCacheServiceImpl;
-
namespace content {
// For use by unit tests.
diff --git a/content/browser/appcache/mock_appcache_storage.cc b/content/browser/appcache/mock_appcache_storage.cc
index ea9673d..91630f7 100644
--- a/content/browser/appcache/mock_appcache_storage.cc
+++ b/content/browser/appcache/mock_appcache_storage.cc
@@ -9,11 +9,11 @@
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
#include "base/stl_util.h"
-#include "webkit/browser/appcache/appcache.h"
-#include "webkit/browser/appcache/appcache_entry.h"
-#include "webkit/browser/appcache/appcache_group.h"
-#include "webkit/browser/appcache/appcache_response.h"
-#include "webkit/browser/appcache/appcache_service_impl.h"
+#include "content/browser/appcache/appcache.h"
+#include "content/browser/appcache/appcache_entry.h"
+#include "content/browser/appcache/appcache_group.h"
+#include "content/browser/appcache/appcache_response.h"
+#include "content/browser/appcache/appcache_service_impl.h"
// This is a quick and easy 'mock' implementation of the storage interface
// that doesn't put anything to disk.
@@ -25,13 +25,6 @@
// happen with a real disk-backed storage impl that involves IO on a
// background thread.
-using appcache::AppCacheResponseWriter;
-using appcache::AppCacheServiceImpl;
-using appcache::APPCACHE_FALLBACK_NAMESPACE;
-using appcache::APPCACHE_INTERCEPT_NAMESPACE;
-using appcache::kAppCacheNoCacheId;
-using appcache::AppCacheNamespaceType;
-
namespace content {
MockAppCacheStorage::MockAppCacheStorage(AppCacheServiceImpl* service)
diff --git a/content/browser/appcache/mock_appcache_storage.h b/content/browser/appcache/mock_appcache_storage.h
index 5d31748..2c4f45583a 100644
--- a/content/browser/appcache/mock_appcache_storage.h
+++ b/content/browser/appcache/mock_appcache_storage.h
@@ -14,22 +14,11 @@
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
-#include "webkit/browser/appcache/appcache.h"
-#include "webkit/browser/appcache/appcache_disk_cache.h"
-#include "webkit/browser/appcache/appcache_group.h"
-#include "webkit/browser/appcache/appcache_response.h"
-#include "webkit/browser/appcache/appcache_storage.h"
-
-using appcache::AppCache;
-using appcache::AppCacheDiskCache;
-using appcache::AppCacheEntry;
-using appcache::AppCacheGroup;
-using appcache::AppCacheInfoCollection;
-using appcache::AppCacheResponseReader;
-using appcache::AppCacheResponseWriter;
-using appcache::AppCacheServiceImpl;
-using appcache::AppCacheStorage;
-using appcache::kAppCacheNoCacheId;
+#include "content/browser/appcache/appcache.h"
+#include "content/browser/appcache/appcache_disk_cache.h"
+#include "content/browser/appcache/appcache_group.h"
+#include "content/browser/appcache/appcache_response.h"
+#include "content/browser/appcache/appcache_storage.h"
namespace content {
FORWARD_DECLARE_TEST(AppCacheServiceImplTest, DeleteAppCachesForOrigin);
diff --git a/content/browser/appcache/mock_appcache_storage_unittest.cc b/content/browser/appcache/mock_appcache_storage_unittest.cc
index f1c9149..c41fe53 100644
--- a/content/browser/appcache/mock_appcache_storage_unittest.cc
+++ b/content/browser/appcache/mock_appcache_storage_unittest.cc
@@ -3,24 +3,12 @@
// found in the LICENSE file.
#include "base/run_loop.h"
+#include "content/browser/appcache/appcache.h"
+#include "content/browser/appcache/appcache_group.h"
+#include "content/browser/appcache/appcache_response.h"
+#include "content/browser/appcache/appcache_storage.h"
#include "content/browser/appcache/mock_appcache_service.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/appcache/appcache.h"
-#include "webkit/browser/appcache/appcache_group.h"
-#include "webkit/browser/appcache/appcache_response.h"
-#include "webkit/browser/appcache/appcache_storage.h"
-
-using appcache::AppCache;
-using appcache::AppCacheEntry;
-using appcache::AppCacheGroup;
-using appcache::AppCacheStorage;
-using appcache::APPCACHE_FALLBACK_NAMESPACE;
-using appcache::APPCACHE_INTERCEPT_NAMESPACE;
-using appcache::kAppCacheNoCacheId;
-using appcache::kAppCacheNoResponseId;
-using appcache::Manifest;
-using appcache::Namespace;
-using appcache::APPCACHE_NETWORK_NAMESPACE;
namespace content {
@@ -483,10 +471,10 @@ TEST_F(MockAppCacheStorageTest, BasicFindMainFallbackResponse) {
Manifest manifest;
manifest.fallback_namespaces.push_back(
- Namespace(APPCACHE_FALLBACK_NAMESPACE, kFallbackNamespaceUrl1,
+ AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE, kFallbackNamespaceUrl1,
kFallbackEntryUrl1, false));
manifest.fallback_namespaces.push_back(
- Namespace(APPCACHE_FALLBACK_NAMESPACE, kFallbackNamespaceUrl2,
+ AppCacheNamespace(APPCACHE_FALLBACK_NAMESPACE, kFallbackNamespaceUrl2,
kFallbackEntryUrl2, false));
scoped_refptr<AppCache> cache(new AppCache(service.storage(), kCacheId));
@@ -595,7 +583,7 @@ TEST_F(MockAppCacheStorageTest, FindMainResponseExclusions) {
Manifest manifest;
manifest.online_whitelist_namespaces.push_back(
- Namespace(APPCACHE_NETWORK_NAMESPACE, kOnlineNamespaceUrl,
+ AppCacheNamespace(APPCACHE_NETWORK_NAMESPACE, kOnlineNamespaceUrl,
GURL(), false));
scoped_refptr<AppCache> cache(new AppCache(service.storage(), kCacheId));
cache->InitializeWithManifest(&manifest);
diff --git a/content/browser/appcache/view_appcache_internals_job.cc b/content/browser/appcache/view_appcache_internals_job.cc
index 0cc64c3..3f8ac4f 100644
--- a/content/browser/appcache/view_appcache_internals_job.cc
+++ b/content/browser/appcache/view_appcache_internals_job.cc
@@ -17,6 +17,12 @@
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
+#include "content/browser/appcache/appcache.h"
+#include "content/browser/appcache/appcache_group.h"
+#include "content/browser/appcache/appcache_policy.h"
+#include "content/browser/appcache/appcache_response.h"
+#include "content/browser/appcache/appcache_service_impl.h"
+#include "content/browser/appcache/appcache_storage.h"
#include "net/base/escape.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
@@ -24,24 +30,6 @@
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_simple_job.h"
#include "net/url_request/view_cache_helper.h"
-#include "webkit/browser/appcache/appcache.h"
-#include "webkit/browser/appcache/appcache_group.h"
-#include "webkit/browser/appcache/appcache_policy.h"
-#include "webkit/browser/appcache/appcache_response.h"
-#include "webkit/browser/appcache/appcache_service_impl.h"
-#include "webkit/browser/appcache/appcache_storage.h"
-
-using appcache::AppCacheGroup;
-using appcache::AppCacheInfo;
-using appcache::AppCacheInfoCollection;
-using appcache::AppCacheInfoVector;
-using appcache::AppCacheServiceImpl;
-using appcache::AppCacheStorage;
-using appcache::AppCacheStorageReference;
-using appcache::AppCacheResourceInfo;
-using appcache::AppCacheResourceInfoVector;
-using appcache::AppCacheResponseInfo;
-using appcache::AppCacheResponseReader;
namespace content {
namespace {
diff --git a/content/browser/appcache/view_appcache_internals_job.h b/content/browser/appcache/view_appcache_internals_job.h
index f11b68e..7fb2ea99 100644
--- a/content/browser/appcache/view_appcache_internals_job.h
+++ b/content/browser/appcache/view_appcache_internals_job.h
@@ -13,7 +13,7 @@ class URLRequest;
class URLRequestJob;
}
-namespace appcache {
+namespace content {
class AppCacheServiceImpl;
}
@@ -24,12 +24,12 @@ class ViewAppCacheInternalsJobFactory {
static net::URLRequestJob* CreateJobForRequest(
net::URLRequest* request,
net::NetworkDelegate* network_delegate,
- appcache::AppCacheServiceImpl* service);
+ AppCacheServiceImpl* service);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(ViewAppCacheInternalsJobFactory);
};
-} // namespace appcache
+} // namespace content
#endif // CONTENT_BROWSER_APPCACHE_VIEW_APPCACHE_INTERNALS_JOB_H_
diff --git a/content/browser/browser_context.cc b/content/browser/browser_context.cc
index dbc5949..1562386 100644
--- a/content/browser/browser_context.cc
+++ b/content/browser/browser_context.cc
@@ -69,7 +69,7 @@ StoragePartition* GetStoragePartitionFromConfig(
void SaveSessionStateOnIOThread(
const scoped_refptr<net::URLRequestContextGetter>& context_getter,
- appcache::AppCacheServiceImpl* appcache_service) {
+ AppCacheServiceImpl* appcache_service) {
net::URLRequestContext* context = context_getter->GetURLRequestContext();
context->cookie_store()->GetCookieMonster()->
SetForceKeepSessionState();
@@ -236,7 +236,7 @@ void BrowserContext::SaveSessionState(BrowserContext* browser_context) {
base::Bind(
&SaveSessionStateOnIOThread,
make_scoped_refptr(browser_context->GetRequestContext()),
- static_cast<appcache::AppCacheServiceImpl*>(
+ static_cast<AppCacheServiceImpl*>(
storage_partition->GetAppCacheService())));
}
diff --git a/content/browser/cross_site_transfer_browsertest.cc b/content/browser/cross_site_transfer_browsertest.cc
index fff180d..9d2d1cc 100644
--- a/content/browser/cross_site_transfer_browsertest.cc
+++ b/content/browser/cross_site_transfer_browsertest.cc
@@ -36,7 +36,7 @@ class TrackingResourceDispatcherHostDelegate
virtual void RequestBeginning(
net::URLRequest* request,
ResourceContext* resource_context,
- appcache::AppCacheService* appcache_service,
+ AppCacheService* appcache_service,
ResourceType::Type resource_type,
int child_id,
int route_id,
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc
index d342ec3..561b215 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -54,6 +54,7 @@
#include "content/browser/streams/stream_registry.h"
#include "content/browser/worker_host/worker_service_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/appcache_interfaces.h"
#include "content/common/resource_messages.h"
#include "content/common/resource_request_body.h"
#include "content/common/ssl_status_serialization.h"
@@ -94,7 +95,6 @@
#include "webkit/browser/blob/blob_url_request_job_factory.h"
#include "webkit/browser/fileapi/file_permission_policy.h"
#include "webkit/browser/fileapi/file_system_context.h"
-#include "webkit/common/appcache/appcache_interfaces.h"
#include "webkit/common/blob/shareable_file_reference.h"
using base::Time;
diff --git a/content/browser/loader/resource_dispatcher_host_unittest.cc b/content/browser/loader/resource_dispatcher_host_unittest.cc
index a815032..18f9c30 100644
--- a/content/browser/loader/resource_dispatcher_host_unittest.cc
+++ b/content/browser/loader/resource_dispatcher_host_unittest.cc
@@ -24,6 +24,7 @@
#include "content/browser/loader/resource_message_filter.h"
#include "content/browser/loader/resource_request_info_impl.h"
#include "content/browser/worker_host/worker_service_impl.h"
+#include "content/common/appcache_interfaces.h"
#include "content/common/child_process_host_impl.h"
#include "content/common/resource_messages.h"
#include "content/common/view_messages.h"
@@ -50,7 +51,6 @@
#include "net/url_request/url_request_test_job.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/common/appcache/appcache_interfaces.h"
#include "webkit/common/blob/shareable_file_reference.h"
// TODO(eroman): Write unit tests for SafeBrowsing that exercise
@@ -139,7 +139,7 @@ static ResourceHostMsg_Request CreateResourceRequest(
request.origin_pid = 0;
request.resource_type = type;
request.request_context = 0;
- request.appcache_host_id = appcache::kAppCacheNoHostId;
+ request.appcache_host_id = kAppCacheNoHostId;
request.download_to_file = false;
request.is_main_frame = true;
request.parent_is_main_frame = false;
@@ -662,7 +662,7 @@ class TestResourceDispatcherHostDelegate
virtual void RequestBeginning(
net::URLRequest* request,
ResourceContext* resource_context,
- appcache::AppCacheService* appcache_service,
+ AppCacheService* appcache_service,
ResourceType::Type resource_type,
int child_id,
int route_id,
diff --git a/content/browser/quota/mock_quota_manager_unittest.cc b/content/browser/quota/mock_quota_manager_unittest.cc
index e1885c4..c62cf6b 100644
--- a/content/browser/quota/mock_quota_manager_unittest.cc
+++ b/content/browser/quota/mock_quota_manager_unittest.cc
@@ -223,4 +223,4 @@ TEST_F(MockQuotaManagerTest, ModifiedOrigins) {
EXPECT_EQ(0UL, origins().count(kOrigin1));
EXPECT_EQ(1UL, origins().count(kOrigin2));
}
-} // Namespace content
+} // AppCacheNamespace content
diff --git a/content/browser/service_worker/service_worker_disk_cache.cc b/content/browser/service_worker/service_worker_disk_cache.cc
index a8ca52a..d31f00c 100644
--- a/content/browser/service_worker/service_worker_disk_cache.cc
+++ b/content/browser/service_worker/service_worker_disk_cache.cc
@@ -8,15 +8,12 @@ namespace content {
ServiceWorkerResponseReader::ServiceWorkerResponseReader(
int64 response_id, ServiceWorkerDiskCache* disk_cache)
- : appcache::AppCacheResponseReader(response_id, 0, disk_cache) {
+ : AppCacheResponseReader(response_id, 0, disk_cache) {
}
ServiceWorkerResponseWriter::ServiceWorkerResponseWriter(
int64 response_id, ServiceWorkerDiskCache* disk_cache)
- : appcache::AppCacheResponseWriter(response_id, 0, disk_cache) {
-}
-
-HttpResponseInfoIOBuffer::~HttpResponseInfoIOBuffer() {
+ : AppCacheResponseWriter(response_id, 0, disk_cache) {
}
} // namespace content
diff --git a/content/browser/service_worker/service_worker_disk_cache.h b/content/browser/service_worker/service_worker_disk_cache.h
index 8385012..7fa51ee 100644
--- a/content/browser/service_worker/service_worker_disk_cache.h
+++ b/content/browser/service_worker/service_worker_disk_cache.h
@@ -5,8 +5,8 @@
#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_DISK_CACHE_H_
#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_DISK_CACHE_H_
+#include "content/browser/appcache/appcache_disk_cache.h"
#include "content/common/content_export.h"
-#include "webkit/browser/appcache/appcache_disk_cache.h"
namespace content {
@@ -17,11 +17,11 @@ namespace content {
// resused classes to a more common location.
class CONTENT_EXPORT ServiceWorkerDiskCache
- : public appcache::AppCacheDiskCache {
+ : public AppCacheDiskCache {
};
class CONTENT_EXPORT ServiceWorkerResponseReader
- : public appcache::AppCacheResponseReader {
+ : public AppCacheResponseReader {
protected:
// Should only be constructed by the storage class.
friend class ServiceWorkerStorage;
@@ -31,7 +31,7 @@ class CONTENT_EXPORT ServiceWorkerResponseReader
};
class CONTENT_EXPORT ServiceWorkerResponseWriter
- : public appcache::AppCacheResponseWriter {
+ : public AppCacheResponseWriter {
protected:
// Should only be constructed by the storage class.
friend class ServiceWorkerStorage;
@@ -40,16 +40,6 @@ class CONTENT_EXPORT ServiceWorkerResponseWriter
ServiceWorkerDiskCache* disk_cache);
};
-struct CONTENT_EXPORT HttpResponseInfoIOBuffer
- : public appcache::HttpResponseInfoIOBuffer {
- public:
- HttpResponseInfoIOBuffer() : appcache::HttpResponseInfoIOBuffer() {}
- explicit HttpResponseInfoIOBuffer(net::HttpResponseInfo* info)
- : appcache::HttpResponseInfoIOBuffer(info) {}
- protected:
- virtual ~HttpResponseInfoIOBuffer();
-};
-
} // namespace content
#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_DISK_CACHE_H_
diff --git a/content/browser/service_worker/service_worker_request_handler.cc b/content/browser/service_worker/service_worker_request_handler.cc
index e3c5ae1..504a7a0d 100644
--- a/content/browser/service_worker/service_worker_request_handler.cc
+++ b/content/browser/service_worker/service_worker_request_handler.cc
@@ -40,13 +40,13 @@ class ServiceWorkerRequestInterceptor
DISALLOW_COPY_AND_ASSIGN(ServiceWorkerRequestInterceptor);
};
-bool IsMethodSupported(const std::string& method) {
+bool IsMethodSupportedForAppCache(const std::string& method) {
return (method == "GET") || (method == "HEAD");
}
-bool IsSchemeAndMethodSupported(const net::URLRequest* request) {
+bool IsSchemeAndMethodSupportedForAppCache(const net::URLRequest* request) {
return request->url().SchemeIsHTTPOrHTTPS() &&
- IsMethodSupported(request->method());
+ IsMethodSupportedForAppCache(request->method());
}
} // namespace
@@ -58,7 +58,7 @@ void ServiceWorkerRequestHandler::InitializeHandler(
int process_id,
int provider_id,
ResourceType::Type resource_type) {
- if (!IsSchemeAndMethodSupported(request)) {
+ if (!IsSchemeAndMethodSupportedForAppCache(request)) {
return;
}
diff --git a/content/browser/storage_partition_impl_map.cc b/content/browser/storage_partition_impl_map.cc
index fdfb34e..d1d2a55 100644
--- a/content/browser/storage_partition_impl_map.cc
+++ b/content/browser/storage_partition_impl_map.cc
@@ -40,7 +40,6 @@
#include "webkit/browser/fileapi/file_system_url_request_job_factory.h"
#include "webkit/common/blob/blob_data.h"
-using appcache::AppCacheServiceImpl;
using fileapi::FileSystemContext;
using webkit_blob::BlobStorageContext;
diff --git a/content/browser/transition_browsertest.cc b/content/browser/transition_browsertest.cc
index 5a6ae9e..79a12a5 100644
--- a/content/browser/transition_browsertest.cc
+++ b/content/browser/transition_browsertest.cc
@@ -41,7 +41,7 @@ class TransitionBrowserTestObserver
virtual void RequestBeginning(
net::URLRequest* request,
ResourceContext* resource_context,
- appcache::AppCacheService* appcache_service,
+ content::AppCacheService* appcache_service,
ResourceType::Type resource_type,
int child_id,
int route_id,
diff --git a/content/browser/webui/url_data_manager_backend.cc b/content/browser/webui/url_data_manager_backend.cc
index 9f661af..369a4e6 100644
--- a/content/browser/webui/url_data_manager_backend.cc
+++ b/content/browser/webui/url_data_manager_backend.cc
@@ -44,8 +44,6 @@
#include "net/url_request/url_request_job_factory.h"
#include "url/url_util.h"
-using appcache::AppCacheServiceImpl;
-
namespace content {
namespace {
diff --git a/content/browser/webui/url_data_manager_backend.h b/content/browser/webui/url_data_manager_backend.h
index a307cdb..03b3d72 100644
--- a/content/browser/webui/url_data_manager_backend.h
+++ b/content/browser/webui/url_data_manager_backend.h
@@ -18,7 +18,7 @@
class GURL;
-namespace appcache {
+namespace content {
class AppCacheServiceImpl;
}
@@ -48,7 +48,7 @@ class URLDataManagerBackend : public base::SupportsUserData::Data {
static net::URLRequestJobFactory::ProtocolHandler* CreateProtocolHandler(
content::ResourceContext* resource_context,
bool is_incognito,
- appcache::AppCacheServiceImpl* appcache_service,
+ AppCacheServiceImpl* appcache_service,
ChromeBlobStorageContext* blob_storage_context);
// Adds a DataSource to the collection of data sources.
diff --git a/content/child/appcache/appcache_backend_proxy.cc b/content/child/appcache/appcache_backend_proxy.cc
index e1d96d6..bf31494 100644
--- a/content/child/appcache/appcache_backend_proxy.cc
+++ b/content/child/appcache/appcache_backend_proxy.cc
@@ -54,8 +54,8 @@ void AppCacheBackendProxy::MarkAsForeignEntry(
cache_document_was_loaded_from));
}
-appcache::AppCacheStatus AppCacheBackendProxy::GetStatus(int host_id) {
- appcache::AppCacheStatus status = appcache::APPCACHE_STATUS_UNCACHED;
+AppCacheStatus AppCacheBackendProxy::GetStatus(int host_id) {
+ AppCacheStatus status = APPCACHE_STATUS_UNCACHED;
sender_->Send(new AppCacheHostMsg_GetStatus(host_id, &status));
return status;
}
@@ -73,7 +73,7 @@ bool AppCacheBackendProxy::SwapCache(int host_id) {
}
void AppCacheBackendProxy::GetResourceList(
- int host_id, std::vector<appcache::AppCacheResourceInfo>* resource_infos) {
+ int host_id, std::vector<AppCacheResourceInfo>* resource_infos) {
sender_->Send(new AppCacheHostMsg_GetResourceList(host_id, resource_infos));
}
diff --git a/content/child/appcache/appcache_backend_proxy.h b/content/child/appcache/appcache_backend_proxy.h
index 389f08c..98eed36 100644
--- a/content/child/appcache/appcache_backend_proxy.h
+++ b/content/child/appcache/appcache_backend_proxy.h
@@ -7,13 +7,13 @@
#include <vector>
+#include "content/common/appcache_interfaces.h"
#include "ipc/ipc_sender.h"
-#include "webkit/common/appcache/appcache_interfaces.h"
namespace content {
// Sends appcache related messages to the main process.
-class AppCacheBackendProxy : public appcache::AppCacheBackend {
+class AppCacheBackendProxy : public AppCacheBackend {
public:
explicit AppCacheBackendProxy(IPC::Sender* sender) : sender_(sender) {}
@@ -38,12 +38,12 @@ class AppCacheBackendProxy : public appcache::AppCacheBackend {
int host_id,
const GURL& document_url,
int64 cache_document_was_loaded_from) OVERRIDE;
- virtual appcache::AppCacheStatus GetStatus(int host_id) OVERRIDE;
+ virtual AppCacheStatus GetStatus(int host_id) OVERRIDE;
virtual bool StartUpdate(int host_id) OVERRIDE;
virtual bool SwapCache(int host_id) OVERRIDE;
virtual void GetResourceList(
int host_id,
- std::vector<appcache::AppCacheResourceInfo>* resource_infos) OVERRIDE;
+ std::vector<AppCacheResourceInfo>* resource_infos) OVERRIDE;
private:
IPC::Sender* sender_;
diff --git a/content/child/appcache/appcache_dispatcher.cc b/content/child/appcache/appcache_dispatcher.cc
index 11e5b9d..02986b1 100644
--- a/content/child/appcache/appcache_dispatcher.cc
+++ b/content/child/appcache/appcache_dispatcher.cc
@@ -10,7 +10,7 @@ namespace content {
AppCacheDispatcher::AppCacheDispatcher(
IPC::Sender* sender,
- appcache::AppCacheFrontend* frontend)
+ AppCacheFrontend* frontend)
: backend_proxy_(sender),
frontend_(frontend) {}
@@ -32,17 +32,17 @@ bool AppCacheDispatcher::OnMessageReceived(const IPC::Message& msg) {
}
void AppCacheDispatcher::OnCacheSelected(
- int host_id, const appcache::AppCacheInfo& info) {
+ int host_id, const AppCacheInfo& info) {
frontend_->OnCacheSelected(host_id, info);
}
void AppCacheDispatcher::OnStatusChanged(const std::vector<int>& host_ids,
- appcache::AppCacheStatus status) {
+ AppCacheStatus status) {
frontend_->OnStatusChanged(host_ids, status);
}
void AppCacheDispatcher::OnEventRaised(const std::vector<int>& host_ids,
- appcache::AppCacheEventID event_id) {
+ AppCacheEventID event_id) {
frontend_->OnEventRaised(host_ids, event_id);
}
@@ -54,14 +54,14 @@ void AppCacheDispatcher::OnProgressEventRaised(
void AppCacheDispatcher::OnErrorEventRaised(
const std::vector<int>& host_ids,
- const appcache::AppCacheErrorDetails& details) {
+ const AppCacheErrorDetails& details) {
frontend_->OnErrorEventRaised(host_ids, details);
}
void AppCacheDispatcher::OnLogMessage(
int host_id, int log_level, const std::string& message) {
frontend_->OnLogMessage(
- host_id, static_cast<appcache::AppCacheLogLevel>(log_level), message);
+ host_id, static_cast<AppCacheLogLevel>(log_level), message);
}
void AppCacheDispatcher::OnContentBlocked(int host_id,
diff --git a/content/child/appcache/appcache_dispatcher.h b/content/child/appcache/appcache_dispatcher.h
index 6b62727..82f4711 100644
--- a/content/child/appcache/appcache_dispatcher.h
+++ b/content/child/appcache/appcache_dispatcher.h
@@ -10,8 +10,8 @@
#include "base/memory/scoped_ptr.h"
#include "content/child/appcache/appcache_backend_proxy.h"
+#include "content/common/appcache_interfaces.h"
#include "ipc/ipc_listener.h"
-#include "webkit/common/appcache/appcache_interfaces.h"
namespace content {
@@ -22,7 +22,7 @@ namespace content {
class AppCacheDispatcher : public IPC::Listener {
public:
AppCacheDispatcher(IPC::Sender* sender,
- appcache::AppCacheFrontend* frontend);
+ AppCacheFrontend* frontend);
virtual ~AppCacheDispatcher();
AppCacheBackendProxy* backend_proxy() { return &backend_proxy_; }
@@ -32,20 +32,20 @@ class AppCacheDispatcher : public IPC::Listener {
private:
// Ipc message handlers
- void OnCacheSelected(int host_id, const appcache::AppCacheInfo& info);
+ void OnCacheSelected(int host_id, const AppCacheInfo& info);
void OnStatusChanged(const std::vector<int>& host_ids,
- appcache::AppCacheStatus status);
+ AppCacheStatus status);
void OnEventRaised(const std::vector<int>& host_ids,
- appcache::AppCacheEventID event_id);
+ AppCacheEventID event_id);
void OnProgressEventRaised(const std::vector<int>& host_ids,
const GURL& url, int num_total, int num_complete);
void OnErrorEventRaised(const std::vector<int>& host_ids,
- const appcache::AppCacheErrorDetails& details);
+ const AppCacheErrorDetails& details);
void OnLogMessage(int host_id, int log_level, const std::string& message);
void OnContentBlocked(int host_id, const GURL& manifest_url);
AppCacheBackendProxy backend_proxy_;
- scoped_ptr<appcache::AppCacheFrontend> frontend_;
+ scoped_ptr<AppCacheFrontend> frontend_;
};
} // namespace content
diff --git a/content/child/appcache/appcache_frontend_impl.cc b/content/child/appcache/appcache_frontend_impl.cc
index 1a33e0d..8ede37a 100644
--- a/content/child/appcache/appcache_frontend_impl.cc
+++ b/content/child/appcache/appcache_frontend_impl.cc
@@ -19,14 +19,14 @@ inline WebApplicationCacheHostImpl* GetHost(int id) {
}
void AppCacheFrontendImpl::OnCacheSelected(int host_id,
- const appcache::AppCacheInfo& info) {
+ const AppCacheInfo& info) {
WebApplicationCacheHostImpl* host = GetHost(host_id);
if (host)
host->OnCacheSelected(info);
}
void AppCacheFrontendImpl::OnStatusChanged(const std::vector<int>& host_ids,
- appcache::AppCacheStatus status) {
+ AppCacheStatus status) {
for (std::vector<int>::const_iterator i = host_ids.begin();
i != host_ids.end(); ++i) {
WebApplicationCacheHostImpl* host = GetHost(*i);
@@ -36,10 +36,10 @@ void AppCacheFrontendImpl::OnStatusChanged(const std::vector<int>& host_ids,
}
void AppCacheFrontendImpl::OnEventRaised(const std::vector<int>& host_ids,
- appcache::AppCacheEventID event_id) {
+ AppCacheEventID event_id) {
DCHECK(event_id !=
- appcache::APPCACHE_PROGRESS_EVENT); // See OnProgressEventRaised.
- DCHECK(event_id != appcache::APPCACHE_ERROR_EVENT); // See OnErrorEventRaised.
+ APPCACHE_PROGRESS_EVENT); // See OnProgressEventRaised.
+ DCHECK(event_id != APPCACHE_ERROR_EVENT); // See OnErrorEventRaised.
for (std::vector<int>::const_iterator i = host_ids.begin();
i != host_ids.end(); ++i) {
WebApplicationCacheHostImpl* host = GetHost(*i);
@@ -63,7 +63,7 @@ void AppCacheFrontendImpl::OnProgressEventRaised(
void AppCacheFrontendImpl::OnErrorEventRaised(
const std::vector<int>& host_ids,
- const appcache::AppCacheErrorDetails& details) {
+ const AppCacheErrorDetails& details) {
for (std::vector<int>::const_iterator i = host_ids.begin();
i != host_ids.end(); ++i) {
WebApplicationCacheHostImpl* host = GetHost(*i);
@@ -73,7 +73,7 @@ void AppCacheFrontendImpl::OnErrorEventRaised(
}
void AppCacheFrontendImpl::OnLogMessage(int host_id,
- appcache::AppCacheLogLevel log_level,
+ AppCacheLogLevel log_level,
const std::string& message) {
WebApplicationCacheHostImpl* host = GetHost(host_id);
if (host)
@@ -90,67 +90,67 @@ void AppCacheFrontendImpl::OnContentBlocked(int host_id,
// Ensure that enum values never get out of sync with the
// ones declared for use within the WebKit api
COMPILE_ASSERT((int)WebApplicationCacheHost::Uncached ==
- (int)appcache::APPCACHE_STATUS_UNCACHED, Uncached);
+ (int)APPCACHE_STATUS_UNCACHED, Uncached);
COMPILE_ASSERT((int)WebApplicationCacheHost::Idle ==
- (int)appcache::APPCACHE_STATUS_IDLE, Idle);
+ (int)APPCACHE_STATUS_IDLE, Idle);
COMPILE_ASSERT((int)WebApplicationCacheHost::Checking ==
- (int)appcache::APPCACHE_STATUS_CHECKING, Checking);
+ (int)APPCACHE_STATUS_CHECKING, Checking);
COMPILE_ASSERT((int)WebApplicationCacheHost::Downloading ==
- (int)appcache::APPCACHE_STATUS_DOWNLOADING, Downloading);
+ (int)APPCACHE_STATUS_DOWNLOADING, Downloading);
COMPILE_ASSERT((int)WebApplicationCacheHost::UpdateReady ==
- (int)appcache::APPCACHE_STATUS_UPDATE_READY, UpdateReady);
+ (int)APPCACHE_STATUS_UPDATE_READY, UpdateReady);
COMPILE_ASSERT((int)WebApplicationCacheHost::Obsolete ==
- (int)appcache::APPCACHE_STATUS_OBSOLETE, Obsolete);
+ (int)APPCACHE_STATUS_OBSOLETE, Obsolete);
COMPILE_ASSERT((int)WebApplicationCacheHost::CheckingEvent ==
- (int)appcache::APPCACHE_CHECKING_EVENT, CheckingEvent);
+ (int)APPCACHE_CHECKING_EVENT, CheckingEvent);
COMPILE_ASSERT((int)WebApplicationCacheHost::ErrorEvent ==
- (int)appcache::APPCACHE_ERROR_EVENT, ErrorEvent);
+ (int)APPCACHE_ERROR_EVENT, ErrorEvent);
COMPILE_ASSERT((int)WebApplicationCacheHost::NoUpdateEvent ==
- (int)appcache::APPCACHE_NO_UPDATE_EVENT, NoUpdateEvent);
+ (int)APPCACHE_NO_UPDATE_EVENT, NoUpdateEvent);
COMPILE_ASSERT((int)WebApplicationCacheHost::DownloadingEvent ==
- (int)appcache::APPCACHE_DOWNLOADING_EVENT, DownloadingEvent);
+ (int)APPCACHE_DOWNLOADING_EVENT, DownloadingEvent);
COMPILE_ASSERT((int)WebApplicationCacheHost::ProgressEvent ==
- (int)appcache::APPCACHE_PROGRESS_EVENT, ProgressEvent);
+ (int)APPCACHE_PROGRESS_EVENT, ProgressEvent);
COMPILE_ASSERT((int)WebApplicationCacheHost::UpdateReadyEvent ==
- (int)appcache::APPCACHE_UPDATE_READY_EVENT, UpdateReadyEvent);
+ (int)APPCACHE_UPDATE_READY_EVENT, UpdateReadyEvent);
COMPILE_ASSERT((int)WebApplicationCacheHost::CachedEvent ==
- (int)appcache::APPCACHE_CACHED_EVENT, CachedEvent);
+ (int)APPCACHE_CACHED_EVENT, CachedEvent);
COMPILE_ASSERT((int)WebApplicationCacheHost::ObsoleteEvent ==
- (int)appcache::APPCACHE_OBSOLETE_EVENT, ObsoleteEvent);
+ (int)APPCACHE_OBSOLETE_EVENT, ObsoleteEvent);
COMPILE_ASSERT((int)WebConsoleMessage::LevelDebug ==
- (int)appcache::APPCACHE_LOG_DEBUG, LevelDebug);
+ (int)APPCACHE_LOG_DEBUG, LevelDebug);
COMPILE_ASSERT((int)WebConsoleMessage::LevelLog ==
- (int)appcache::APPCACHE_LOG_INFO, LevelLog);
+ (int)APPCACHE_LOG_INFO, LevelLog);
COMPILE_ASSERT((int)WebConsoleMessage::LevelWarning ==
- (int)appcache::APPCACHE_LOG_WARNING, LevelWarning);
+ (int)APPCACHE_LOG_WARNING, LevelWarning);
COMPILE_ASSERT((int)WebConsoleMessage::LevelError ==
- (int)appcache::APPCACHE_LOG_ERROR, LevelError);
+ (int)APPCACHE_LOG_ERROR, LevelError);
COMPILE_ASSERT((int)WebApplicationCacheHost::ManifestError ==
- (int)appcache::APPCACHE_MANIFEST_ERROR,
+ (int)APPCACHE_MANIFEST_ERROR,
ManifestError);
COMPILE_ASSERT((int)WebApplicationCacheHost::SignatureError ==
- (int)appcache::APPCACHE_SIGNATURE_ERROR,
+ (int)APPCACHE_SIGNATURE_ERROR,
SignatureError);
COMPILE_ASSERT((int)WebApplicationCacheHost::ResourceError ==
- (int)appcache::APPCACHE_RESOURCE_ERROR,
+ (int)APPCACHE_RESOURCE_ERROR,
ResourceError);
COMPILE_ASSERT((int)WebApplicationCacheHost::ChangedError ==
- (int)appcache::APPCACHE_CHANGED_ERROR,
+ (int)APPCACHE_CHANGED_ERROR,
ChangedError);
COMPILE_ASSERT((int)WebApplicationCacheHost::AbortError ==
- (int)appcache::APPCACHE_ABORT_ERROR,
+ (int)APPCACHE_ABORT_ERROR,
AbortError);
COMPILE_ASSERT((int)WebApplicationCacheHost::QuotaError ==
- (int)appcache::APPCACHE_QUOTA_ERROR,
+ (int)APPCACHE_QUOTA_ERROR,
QuotaError);
COMPILE_ASSERT((int)WebApplicationCacheHost::PolicyError ==
- (int)appcache::APPCACHE_POLICY_ERROR,
+ (int)APPCACHE_POLICY_ERROR,
PolicyError);
COMPILE_ASSERT((int)WebApplicationCacheHost::UnknownError ==
- (int)appcache::APPCACHE_UNKNOWN_ERROR,
+ (int)APPCACHE_UNKNOWN_ERROR,
UnknownError);
} // namespace content
diff --git a/content/child/appcache/appcache_frontend_impl.h b/content/child/appcache/appcache_frontend_impl.h
index 90ac4fe..161f661 100644
--- a/content/child/appcache/appcache_frontend_impl.h
+++ b/content/child/appcache/appcache_frontend_impl.h
@@ -5,27 +5,27 @@
#ifndef CONTENT_CHILD_APPCACHE_APPCACHE_FRONTEND_IMPL_H_
#define CONTENT_CHILD_APPCACHE_APPCACHE_FRONTEND_IMPL_H_
-#include "webkit/common/appcache/appcache_interfaces.h"
+#include "content/common/appcache_interfaces.h"
namespace content {
-class AppCacheFrontendImpl : public appcache::AppCacheFrontend {
+class AppCacheFrontendImpl : public AppCacheFrontend {
public:
virtual void OnCacheSelected(int host_id,
- const appcache::AppCacheInfo& info) OVERRIDE;
+ const AppCacheInfo& info) OVERRIDE;
virtual void OnStatusChanged(const std::vector<int>& host_ids,
- appcache::AppCacheStatus status) OVERRIDE;
+ AppCacheStatus status) OVERRIDE;
virtual void OnEventRaised(const std::vector<int>& host_ids,
- appcache::AppCacheEventID event_id) OVERRIDE;
+ AppCacheEventID event_id) OVERRIDE;
virtual void OnProgressEventRaised(const std::vector<int>& host_ids,
const GURL& url,
int num_total,
int num_complete) OVERRIDE;
virtual void OnErrorEventRaised(const std::vector<int>& host_ids,
- const appcache::AppCacheErrorDetails& details)
+ const AppCacheErrorDetails& details)
OVERRIDE;
virtual void OnLogMessage(int host_id,
- appcache::AppCacheLogLevel log_level,
+ AppCacheLogLevel log_level,
const std::string& message) OVERRIDE;
virtual void OnContentBlocked(int host_id, const GURL& manifest_url) OVERRIDE;
};
diff --git a/content/child/appcache/web_application_cache_host_impl.cc b/content/child/appcache/web_application_cache_host_impl.cc
index 427262e..321f467 100644
--- a/content/child/appcache/web_application_cache_host_impl.cc
+++ b/content/child/appcache/web_application_cache_host_impl.cc
@@ -20,8 +20,6 @@ using blink::WebURLRequest;
using blink::WebURL;
using blink::WebURLResponse;
using blink::WebVector;
-using appcache::AppCacheBackend;
-using appcache::AppCacheResourceInfo;
namespace content {
@@ -61,12 +59,12 @@ WebApplicationCacheHostImpl::WebApplicationCacheHostImpl(
: client_(client),
backend_(backend),
host_id_(all_hosts()->Add(this)),
- status_(appcache::APPCACHE_STATUS_UNCACHED),
+ status_(APPCACHE_STATUS_UNCACHED),
is_scheme_supported_(false),
is_get_method_(false),
is_new_master_entry_(MAYBE),
was_select_cache_called_(false) {
- DCHECK(client && backend && (host_id_ != appcache::kAppCacheNoHostId));
+ DCHECK(client && backend && (host_id_ != kAppCacheNoHostId));
backend_->RegisterHost(host_id_);
}
@@ -77,45 +75,45 @@ WebApplicationCacheHostImpl::~WebApplicationCacheHostImpl() {
}
void WebApplicationCacheHostImpl::OnCacheSelected(
- const appcache::AppCacheInfo& info) {
+ const AppCacheInfo& info) {
cache_info_ = info;
client_->didChangeCacheAssociation();
}
void WebApplicationCacheHostImpl::OnStatusChanged(
- appcache::AppCacheStatus status) {
+ AppCacheStatus status) {
// TODO(michaeln): delete me, not used
}
void WebApplicationCacheHostImpl::OnEventRaised(
- appcache::AppCacheEventID event_id) {
+ AppCacheEventID event_id) {
DCHECK(event_id !=
- appcache::APPCACHE_PROGRESS_EVENT); // See OnProgressEventRaised.
- DCHECK(event_id != appcache::APPCACHE_ERROR_EVENT); // See OnErrorEventRaised.
+ APPCACHE_PROGRESS_EVENT); // See OnProgressEventRaised.
+ DCHECK(event_id != APPCACHE_ERROR_EVENT); // See OnErrorEventRaised.
// Emit logging output prior to calling out to script as we can get
// deleted within the script event handler.
const char* kFormatString = "Application Cache %s event";
std::string message = base::StringPrintf(kFormatString,
kEventNames[event_id]);
- OnLogMessage(appcache::APPCACHE_LOG_INFO, message);
+ OnLogMessage(APPCACHE_LOG_INFO, message);
switch (event_id) {
- case appcache::APPCACHE_CHECKING_EVENT:
- status_ = appcache::APPCACHE_STATUS_CHECKING;
+ case APPCACHE_CHECKING_EVENT:
+ status_ = APPCACHE_STATUS_CHECKING;
break;
- case appcache::APPCACHE_DOWNLOADING_EVENT:
- status_ = appcache::APPCACHE_STATUS_DOWNLOADING;
+ case APPCACHE_DOWNLOADING_EVENT:
+ status_ = APPCACHE_STATUS_DOWNLOADING;
break;
- case appcache::APPCACHE_UPDATE_READY_EVENT:
- status_ = appcache::APPCACHE_STATUS_UPDATE_READY;
+ case APPCACHE_UPDATE_READY_EVENT:
+ status_ = APPCACHE_STATUS_UPDATE_READY;
break;
- case appcache::APPCACHE_CACHED_EVENT:
- case appcache::APPCACHE_NO_UPDATE_EVENT:
- status_ = appcache::APPCACHE_STATUS_IDLE;
+ case APPCACHE_CACHED_EVENT:
+ case APPCACHE_NO_UPDATE_EVENT:
+ status_ = APPCACHE_STATUS_IDLE;
break;
- case appcache::APPCACHE_OBSOLETE_EVENT:
- status_ = appcache::APPCACHE_STATUS_OBSOLETE;
+ case APPCACHE_OBSOLETE_EVENT:
+ status_ = APPCACHE_STATUS_OBSOLETE;
break;
default:
NOTREACHED();
@@ -132,25 +130,25 @@ void WebApplicationCacheHostImpl::OnProgressEventRaised(
const char* kFormatString = "Application Cache Progress event (%d of %d) %s";
std::string message = base::StringPrintf(kFormatString, num_complete,
num_total, url.spec().c_str());
- OnLogMessage(appcache::APPCACHE_LOG_INFO, message);
- status_ = appcache::APPCACHE_STATUS_DOWNLOADING;
+ OnLogMessage(APPCACHE_LOG_INFO, message);
+ status_ = APPCACHE_STATUS_DOWNLOADING;
client_->notifyProgressEventListener(url, num_total, num_complete);
}
void WebApplicationCacheHostImpl::OnErrorEventRaised(
- const appcache::AppCacheErrorDetails& details) {
+ const AppCacheErrorDetails& details) {
// Emit logging output prior to calling out to script as we can get
// deleted within the script event handler.
const char* kFormatString = "Application Cache Error event: %s";
std::string full_message =
base::StringPrintf(kFormatString, details.message.c_str());
- OnLogMessage(appcache::APPCACHE_LOG_ERROR, full_message);
+ OnLogMessage(APPCACHE_LOG_ERROR, full_message);
- status_ = cache_info_.is_complete ? appcache::APPCACHE_STATUS_IDLE :
- appcache::APPCACHE_STATUS_UNCACHED;
+ status_ = cache_info_.is_complete ? APPCACHE_STATUS_IDLE :
+ APPCACHE_STATUS_UNCACHED;
if (details.is_cross_origin) {
// Don't leak detailed information to script for cross-origin resources.
- DCHECK_EQ(appcache::APPCACHE_RESOURCE_ERROR, details.reason);
+ DCHECK_EQ(APPCACHE_RESOURCE_ERROR, details.reason);
client_->notifyErrorEventListener(
static_cast<ErrorReason>(details.reason), details.url, 0, WebString());
} else {
@@ -168,13 +166,13 @@ void WebApplicationCacheHostImpl::willStartMainResourceRequest(
original_main_resource_url_ = ClearUrlRef(request.url());
std::string method = request.httpMethod().utf8();
- is_get_method_ = (method == appcache::kHttpGETMethod);
+ is_get_method_ = (method == kHttpGETMethod);
DCHECK(method == StringToUpperASCII(method));
const WebApplicationCacheHostImpl* spawning_host_impl =
static_cast<const WebApplicationCacheHostImpl*>(spawning_host);
if (spawning_host_impl && (spawning_host_impl != this) &&
- (spawning_host_impl->status_ != appcache::APPCACHE_STATUS_UNCACHED)) {
+ (spawning_host_impl->status_ != APPCACHE_STATUS_UNCACHED)) {
backend_->SetSpawningHostId(host_id_, spawning_host_impl->host_id());
}
}
@@ -189,8 +187,8 @@ void WebApplicationCacheHostImpl::selectCacheWithoutManifest() {
return;
was_select_cache_called_ = true;
- status_ = (document_response_.appCacheID() == appcache::kAppCacheNoCacheId) ?
- appcache::APPCACHE_STATUS_UNCACHED : appcache::APPCACHE_STATUS_CHECKING;
+ status_ = (document_response_.appCacheID() == kAppCacheNoCacheId) ?
+ APPCACHE_STATUS_UNCACHED : APPCACHE_STATUS_CHECKING;
is_new_master_entry_ = NO;
backend_->SelectCache(host_id_, document_url_,
document_response_.appCacheID(),
@@ -207,18 +205,18 @@ bool WebApplicationCacheHostImpl::selectCacheWithManifest(
// 6.9.6 The application cache selection algorithm
// Check for new 'master' entries.
- if (document_response_.appCacheID() == appcache::kAppCacheNoCacheId) {
+ if (document_response_.appCacheID() == kAppCacheNoCacheId) {
if (is_scheme_supported_ && is_get_method_ &&
(manifest_gurl.GetOrigin() == document_url_.GetOrigin())) {
- status_ = appcache::APPCACHE_STATUS_CHECKING;
+ status_ = APPCACHE_STATUS_CHECKING;
is_new_master_entry_ = YES;
} else {
- status_ = appcache::APPCACHE_STATUS_UNCACHED;
+ status_ = APPCACHE_STATUS_UNCACHED;
is_new_master_entry_ = NO;
manifest_gurl = GURL();
}
backend_->SelectCache(
- host_id_, document_url_, appcache::kAppCacheNoCacheId, manifest_gurl);
+ host_id_, document_url_, kAppCacheNoCacheId, manifest_gurl);
return true;
}
@@ -230,11 +228,11 @@ bool WebApplicationCacheHostImpl::selectCacheWithManifest(
if (document_manifest_gurl != manifest_gurl) {
backend_->MarkAsForeignEntry(host_id_, document_url_,
document_response_.appCacheID());
- status_ = appcache::APPCACHE_STATUS_UNCACHED;
+ status_ = APPCACHE_STATUS_UNCACHED;
return false; // the navigation will be restarted
}
- status_ = appcache::APPCACHE_STATUS_CHECKING;
+ status_ = APPCACHE_STATUS_CHECKING;
// Its a 'master' entry thats already in the cache.
backend_->SelectCache(host_id_, document_url_,
@@ -251,8 +249,8 @@ void WebApplicationCacheHostImpl::didReceiveResponseForMainResource(
is_get_method_ = true; // A redirect was involved.
original_main_resource_url_ = GURL();
- is_scheme_supported_ = appcache::IsSchemeSupported(document_url_);
- if ((document_response_.appCacheID() != appcache::kAppCacheNoCacheId) ||
+ is_scheme_supported_ = IsSchemeSupportedForAppCache(document_url_);
+ if ((document_response_.appCacheID() != kAppCacheNoCacheId) ||
!is_scheme_supported_ || !is_get_method_)
is_new_master_entry_ = NO;
}
@@ -277,9 +275,9 @@ WebApplicationCacheHost::Status WebApplicationCacheHostImpl::status() {
bool WebApplicationCacheHostImpl::startUpdate() {
if (!backend_->StartUpdate(host_id_))
return false;
- if (status_ == appcache::APPCACHE_STATUS_IDLE ||
- status_ == appcache::APPCACHE_STATUS_UPDATE_READY)
- status_ = appcache::APPCACHE_STATUS_CHECKING;
+ if (status_ == APPCACHE_STATUS_IDLE ||
+ status_ == APPCACHE_STATUS_UPDATE_READY)
+ status_ = APPCACHE_STATUS_CHECKING;
else
status_ = backend_->GetStatus(host_id_);
return true;
diff --git a/content/child/appcache/web_application_cache_host_impl.h b/content/child/appcache/web_application_cache_host_impl.h
index 07ec2db..fe27be2 100644
--- a/content/child/appcache/web_application_cache_host_impl.h
+++ b/content/child/appcache/web_application_cache_host_impl.h
@@ -7,12 +7,12 @@
#include <string>
+#include "content/common/appcache_interfaces.h"
#include "third_party/WebKit/public/platform/WebApplicationCacheHost.h"
#include "third_party/WebKit/public/platform/WebApplicationCacheHostClient.h"
#include "third_party/WebKit/public/platform/WebURLResponse.h"
#include "third_party/WebKit/public/platform/WebVector.h"
#include "url/gurl.h"
-#include "webkit/common/appcache/appcache_interfaces.h"
namespace content {
@@ -23,19 +23,19 @@ class WebApplicationCacheHostImpl
static WebApplicationCacheHostImpl* FromId(int id);
WebApplicationCacheHostImpl(blink::WebApplicationCacheHostClient* client,
- appcache::AppCacheBackend* backend);
+ AppCacheBackend* backend);
virtual ~WebApplicationCacheHostImpl();
int host_id() const { return host_id_; }
- appcache::AppCacheBackend* backend() const { return backend_; }
+ AppCacheBackend* backend() const { return backend_; }
blink::WebApplicationCacheHostClient* client() const { return client_; }
- virtual void OnCacheSelected(const appcache::AppCacheInfo& info);
- void OnStatusChanged(appcache::AppCacheStatus);
- void OnEventRaised(appcache::AppCacheEventID);
+ virtual void OnCacheSelected(const AppCacheInfo& info);
+ void OnStatusChanged(AppCacheStatus);
+ void OnEventRaised(AppCacheEventID);
void OnProgressEventRaised(const GURL& url, int num_total, int num_complete);
- void OnErrorEventRaised(const appcache::AppCacheErrorDetails& details);
- virtual void OnLogMessage(appcache::AppCacheLogLevel log_level,
+ void OnErrorEventRaised(const AppCacheErrorDetails& details);
+ virtual void OnLogMessage(AppCacheLogLevel log_level,
const std::string& message) {}
virtual void OnContentBlocked(const GURL& manifest_url) {}
@@ -62,15 +62,15 @@ class WebApplicationCacheHostImpl
};
blink::WebApplicationCacheHostClient* client_;
- appcache::AppCacheBackend* backend_;
+ AppCacheBackend* backend_;
int host_id_;
- appcache::AppCacheStatus status_;
+ AppCacheStatus status_;
blink::WebURLResponse document_response_;
GURL document_url_;
bool is_scheme_supported_;
bool is_get_method_;
IsNewMasterEntry is_new_master_entry_;
- appcache::AppCacheInfo cache_info_;
+ AppCacheInfo cache_info_;
GURL original_main_resource_url_; // Used to detect redirection.
bool was_select_cache_called_;
};
diff --git a/content/child/resource_dispatcher_unittest.cc b/content/child/resource_dispatcher_unittest.cc
index 86dc17f..3e427ca 100644
--- a/content/child/resource_dispatcher_unittest.cc
+++ b/content/child/resource_dispatcher_unittest.cc
@@ -16,6 +16,7 @@
#include "content/child/request_extra_data.h"
#include "content/child/request_info.h"
#include "content/child/resource_dispatcher.h"
+#include "content/common/appcache_interfaces.h"
#include "content/common/resource_messages.h"
#include "content/common/service_worker/service_worker_types.h"
#include "content/public/child/request_peer.h"
@@ -24,7 +25,6 @@
#include "net/http/http_response_headers.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webkit/child/resource_loader_bridge.h"
-#include "webkit/common/appcache/appcache_interfaces.h"
using webkit_glue::ResourceLoaderBridge;
@@ -331,7 +331,7 @@ class ResourceDispatcherTest : public testing::Test, public IPC::Sender {
request_info.load_flags = 0;
request_info.requestor_pid = 0;
request_info.request_type = ResourceType::SUB_RESOURCE;
- request_info.appcache_host_id = appcache::kAppCacheNoHostId;
+ request_info.appcache_host_id = kAppCacheNoHostId;
request_info.routing_id = 0;
request_info.download_to_file = download_to_file;
RequestExtraData extra_data;
diff --git a/content/common/appcache_interfaces.cc b/content/common/appcache_interfaces.cc
new file mode 100644
index 0000000..f9f9a05
--- /dev/null
+++ b/content/common/appcache_interfaces.cc
@@ -0,0 +1,139 @@
+// Copyright (c) 2012 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 "content/common/appcache_interfaces.h"
+
+#include <set>
+
+#include "base/strings/string_util.h"
+#include "net/url_request/url_request.h"
+#include "url/gurl.h"
+
+namespace content {
+
+const char kHttpScheme[] = "http";
+const char kHttpsScheme[] = "https";
+const char kDevToolsScheme[] = "chrome-devtools";
+const char kHttpGETMethod[] = "GET";
+const char kHttpHEADMethod[] = "HEAD";
+
+const char kEnableExecutableHandlers[] = "enable-appcache-executable-handlers";
+
+const base::FilePath::CharType kAppCacheDatabaseName[] =
+ FILE_PATH_LITERAL("Index");
+
+AppCacheInfo::AppCacheInfo()
+ : cache_id(kAppCacheNoCacheId),
+ group_id(0),
+ status(APPCACHE_STATUS_UNCACHED),
+ size(0),
+ is_complete(false) {
+}
+
+AppCacheInfo::~AppCacheInfo() {
+}
+
+AppCacheResourceInfo::AppCacheResourceInfo()
+ : url(),
+ size(0),
+ is_master(false),
+ is_manifest(false),
+ is_intercept(false),
+ is_fallback(false),
+ is_foreign(false),
+ is_explicit(false),
+ response_id(kAppCacheNoResponseId) {
+}
+
+AppCacheResourceInfo::~AppCacheResourceInfo() {
+}
+
+AppCacheErrorDetails::AppCacheErrorDetails()
+ : message(),
+ reason(APPCACHE_UNKNOWN_ERROR),
+ url(),
+ status(0),
+ is_cross_origin(false) {}
+
+AppCacheErrorDetails::AppCacheErrorDetails(
+ std::string in_message,
+ AppCacheErrorReason in_reason,
+ GURL in_url,
+ int in_status,
+ bool in_is_cross_origin)
+ : message(in_message),
+ reason(in_reason),
+ url(in_url),
+ status(in_status),
+ is_cross_origin(in_is_cross_origin) {}
+
+AppCacheErrorDetails::~AppCacheErrorDetails() {}
+
+AppCacheNamespace::AppCacheNamespace()
+ : type(APPCACHE_FALLBACK_NAMESPACE),
+ is_pattern(false),
+ is_executable(false) {
+}
+
+AppCacheNamespace::AppCacheNamespace(
+ AppCacheNamespaceType type, const GURL& url, const GURL& target,
+ bool is_pattern)
+ : type(type),
+ namespace_url(url),
+ target_url(target),
+ is_pattern(is_pattern),
+ is_executable(false) {
+}
+
+AppCacheNamespace::AppCacheNamespace(
+ AppCacheNamespaceType type, const GURL& url, const GURL& target,
+ bool is_pattern, bool is_executable)
+ : type(type),
+ namespace_url(url),
+ target_url(target),
+ is_pattern(is_pattern),
+ is_executable(is_executable) {
+}
+
+AppCacheNamespace::~AppCacheNamespace() {
+}
+
+bool AppCacheNamespace::IsMatch(const GURL& url) const {
+ if (is_pattern) {
+ // We have to escape '?' characters since MatchPattern also treats those
+ // as wildcards which we don't want here, we only do '*'s.
+ std::string pattern = namespace_url.spec();
+ if (namespace_url.has_query())
+ ReplaceSubstringsAfterOffset(&pattern, 0, "?", "\\?");
+ return MatchPattern(url.spec(), pattern);
+ }
+ return StartsWithASCII(url.spec(), namespace_url.spec(), true);
+}
+
+bool IsSchemeSupportedForAppCache(const GURL& url) {
+ bool supported = url.SchemeIs(kHttpScheme) || url.SchemeIs(kHttpsScheme) ||
+ url.SchemeIs(kDevToolsScheme);
+
+#ifndef NDEBUG
+ // TODO(michaeln): It would be really nice if this could optionally work for
+ // file and filesystem urls too to help web developers experiment and test
+ // their apps, perhaps enabled via a cmd line flag or some other developer
+ // tool setting. Unfortunately file scheme net::URLRequests don't produce the
+ // same signalling (200 response codes, headers) as http URLRequests, so this
+ // doesn't work just yet.
+ // supported |= url.SchemeIsFile();
+#endif
+ return supported;
+}
+
+bool IsMethodSupportedForAppCache(const std::string& method) {
+ return (method == kHttpGETMethod) || (method == kHttpHEADMethod);
+}
+
+bool IsSchemeAndMethodSupportedForAppCache(const net::URLRequest* request) {
+ return IsSchemeSupportedForAppCache(request->url()) &&
+ IsMethodSupportedForAppCache(request->method());
+}
+
+} // namespace content
diff --git a/content/common/appcache_interfaces.h b/content/common/appcache_interfaces.h
new file mode 100644
index 0000000..7199fbb
--- /dev/null
+++ b/content/common/appcache_interfaces.h
@@ -0,0 +1,188 @@
+// Copyright (c) 2012 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.
+
+#ifndef CONTENT_COMMON_APPCACHE_APPCACHE_INTERFACES_H_
+#define CONTENT_COMMON_APPCACHE_APPCACHE_INTERFACES_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "content/public/common/appcache_info.h"
+
+namespace net {
+class URLRequest;
+}
+
+namespace content {
+
+// Defines constants, types, and abstract classes used in the main
+// process and in child processes.
+
+enum AppCacheEventID {
+ APPCACHE_CHECKING_EVENT,
+ APPCACHE_ERROR_EVENT,
+ APPCACHE_NO_UPDATE_EVENT,
+ APPCACHE_DOWNLOADING_EVENT,
+ APPCACHE_PROGRESS_EVENT,
+ APPCACHE_UPDATE_READY_EVENT,
+ APPCACHE_CACHED_EVENT,
+ APPCACHE_OBSOLETE_EVENT,
+ APPCACHE_EVENT_ID_LAST = APPCACHE_OBSOLETE_EVENT
+};
+
+// Temporarily renumber them in wierd way, to help remove LOG_TIP from WebKit
+enum AppCacheLogLevel {
+ APPCACHE_LOG_DEBUG = 4,
+ APPCACHE_LOG_INFO = 1,
+ APPCACHE_LOG_WARNING = 2,
+ APPCACHE_LOG_ERROR = 3,
+};
+
+enum AppCacheNamespaceType {
+ APPCACHE_FALLBACK_NAMESPACE,
+ APPCACHE_INTERCEPT_NAMESPACE,
+ APPCACHE_NETWORK_NAMESPACE
+};
+
+enum AppCacheErrorReason {
+ APPCACHE_MANIFEST_ERROR,
+ APPCACHE_SIGNATURE_ERROR,
+ APPCACHE_RESOURCE_ERROR,
+ APPCACHE_CHANGED_ERROR,
+ APPCACHE_ABORT_ERROR,
+ APPCACHE_QUOTA_ERROR,
+ APPCACHE_POLICY_ERROR,
+ APPCACHE_UNKNOWN_ERROR,
+ APPCACHE_ERROR_REASON_LAST = APPCACHE_UNKNOWN_ERROR
+};
+
+// Type to hold information about a single appcache resource.
+struct CONTENT_EXPORT AppCacheResourceInfo {
+ AppCacheResourceInfo();
+ ~AppCacheResourceInfo();
+
+ GURL url;
+ int64 size;
+ bool is_master;
+ bool is_manifest;
+ bool is_intercept;
+ bool is_fallback;
+ bool is_foreign;
+ bool is_explicit;
+ int64 response_id;
+};
+
+struct CONTENT_EXPORT AppCacheErrorDetails {
+ AppCacheErrorDetails();
+ AppCacheErrorDetails(std::string message,
+ AppCacheErrorReason reason,
+ GURL url,
+ int status,
+ bool is_cross_origin);
+ ~AppCacheErrorDetails();
+
+ std::string message;
+ AppCacheErrorReason reason;
+ GURL url;
+ int status;
+ bool is_cross_origin;
+};
+
+typedef std::vector<AppCacheResourceInfo> AppCacheResourceInfoVector;
+
+struct CONTENT_EXPORT AppCacheNamespace {
+ AppCacheNamespace(); // Type is APPCACHE_FALLBACK_NAMESPACE by default.
+ AppCacheNamespace(AppCacheNamespaceType type, const GURL& url,
+ const GURL& target, bool is_pattern);
+ AppCacheNamespace(AppCacheNamespaceType type, const GURL& url,
+ const GURL& target, bool is_pattern, bool is_executable);
+ ~AppCacheNamespace();
+
+ bool IsMatch(const GURL& url) const;
+
+ AppCacheNamespaceType type;
+ GURL namespace_url;
+ GURL target_url;
+ bool is_pattern;
+ bool is_executable;
+};
+
+typedef std::vector<AppCacheNamespace> AppCacheNamespaceVector;
+
+// Interface used by backend (browser-process) to talk to frontend (renderer).
+class CONTENT_EXPORT AppCacheFrontend {
+ public:
+ virtual void OnCacheSelected(
+ int host_id, const AppCacheInfo& info) = 0;
+ virtual void OnStatusChanged(const std::vector<int>& host_ids,
+ AppCacheStatus status) = 0;
+ virtual void OnEventRaised(const std::vector<int>& host_ids,
+ AppCacheEventID event_id) = 0;
+ virtual void OnProgressEventRaised(const std::vector<int>& host_ids,
+ const GURL& url,
+ int num_total, int num_complete) = 0;
+ virtual void OnErrorEventRaised(
+ const std::vector<int>& host_ids,
+ const AppCacheErrorDetails& details) = 0;
+ virtual void OnContentBlocked(int host_id,
+ const GURL& manifest_url) = 0;
+ virtual void OnLogMessage(int host_id, AppCacheLogLevel log_level,
+ const std::string& message) = 0;
+ virtual ~AppCacheFrontend() {}
+};
+
+// Interface used by frontend (renderer) to talk to backend (browser-process).
+class CONTENT_EXPORT AppCacheBackend {
+ public:
+ virtual void RegisterHost(int host_id) = 0;
+ virtual void UnregisterHost(int host_id) = 0;
+ virtual void SetSpawningHostId(int host_id, int spawning_host_id) = 0;
+ virtual void SelectCache(int host_id,
+ const GURL& document_url,
+ const int64 cache_document_was_loaded_from,
+ const GURL& manifest_url) = 0;
+ virtual void SelectCacheForWorker(
+ int host_id,
+ int parent_process_id,
+ int parent_host_id) = 0;
+ virtual void SelectCacheForSharedWorker(
+ int host_id,
+ int64 appcache_id) = 0;
+ virtual void MarkAsForeignEntry(int host_id, const GURL& document_url,
+ int64 cache_document_was_loaded_from) = 0;
+ virtual AppCacheStatus GetStatus(int host_id) = 0;
+ virtual bool StartUpdate(int host_id) = 0;
+ virtual bool SwapCache(int host_id) = 0;
+ virtual void GetResourceList(
+ int host_id, std::vector<AppCacheResourceInfo>* resource_infos) = 0;
+
+ protected:
+ virtual ~AppCacheBackend() {}
+};
+
+// Useful string constants.
+// Note: These are also defined elsewhere in the chrome code base in
+// url_contants.h .cc, however the content library can not have
+// any dependencies on the chrome library, so we can't use them in here.
+CONTENT_EXPORT extern const char kHttpScheme[];
+CONTENT_EXPORT extern const char kHttpsScheme[];
+CONTENT_EXPORT extern const char kHttpGETMethod[];
+CONTENT_EXPORT extern const char kHttpHEADMethod[];
+
+// CommandLine flag to turn this experimental feature on.
+CONTENT_EXPORT extern const char kEnableExecutableHandlers[];
+
+CONTENT_EXPORT bool IsSchemeSupportedForAppCache(const GURL& url);
+CONTENT_EXPORT bool IsMethodSupportedForAppCache(
+ const std::string& method);
+CONTENT_EXPORT bool IsSchemeAndMethodSupportedForAppCache(
+ const net::URLRequest* request);
+
+CONTENT_EXPORT extern const base::FilePath::CharType
+ kAppCacheDatabaseName[];
+
+} // namespace
+
+#endif // CONTENT_COMMON_APPCACHE_APPCACHE_INTERFACES_H_
diff --git a/content/common/appcache_messages.h b/content/common/appcache_messages.h
index 8ad8fd5..15d3758 100644
--- a/content/common/appcache_messages.h
+++ b/content/common/appcache_messages.h
@@ -5,18 +5,18 @@
// Multiply-included message file, hence no include guard.
#include "ipc/ipc_message_macros.h"
-#include "webkit/common/appcache/appcache_interfaces.h"
+#include "content/common/appcache_interfaces.h"
#define IPC_MESSAGE_START AppCacheMsgStart
-IPC_ENUM_TRAITS_MAX_VALUE(appcache::AppCacheEventID,
- appcache::APPCACHE_EVENT_ID_LAST)
-IPC_ENUM_TRAITS_MAX_VALUE(appcache::AppCacheStatus,
- appcache::APPCACHE_STATUS_LAST)
-IPC_ENUM_TRAITS_MAX_VALUE(appcache::AppCacheErrorReason,
- appcache::APPCACHE_ERROR_REASON_LAST)
+IPC_ENUM_TRAITS_MAX_VALUE(content::AppCacheEventID,
+ content::APPCACHE_EVENT_ID_LAST)
+IPC_ENUM_TRAITS_MAX_VALUE(content::AppCacheStatus,
+ content::APPCACHE_STATUS_LAST)
+IPC_ENUM_TRAITS_MAX_VALUE(content::AppCacheErrorReason,
+ content::APPCACHE_ERROR_REASON_LAST)
-IPC_STRUCT_TRAITS_BEGIN(appcache::AppCacheInfo)
+IPC_STRUCT_TRAITS_BEGIN(content::AppCacheInfo)
IPC_STRUCT_TRAITS_MEMBER(manifest_url)
IPC_STRUCT_TRAITS_MEMBER(creation_time)
IPC_STRUCT_TRAITS_MEMBER(last_update_time)
@@ -28,7 +28,7 @@ IPC_STRUCT_TRAITS_BEGIN(appcache::AppCacheInfo)
IPC_STRUCT_TRAITS_MEMBER(is_complete)
IPC_STRUCT_TRAITS_END()
-IPC_STRUCT_TRAITS_BEGIN(appcache::AppCacheResourceInfo)
+IPC_STRUCT_TRAITS_BEGIN(content::AppCacheResourceInfo)
IPC_STRUCT_TRAITS_MEMBER(url)
IPC_STRUCT_TRAITS_MEMBER(size)
IPC_STRUCT_TRAITS_MEMBER(is_master)
@@ -38,7 +38,7 @@ IPC_STRUCT_TRAITS_BEGIN(appcache::AppCacheResourceInfo)
IPC_STRUCT_TRAITS_MEMBER(is_explicit)
IPC_STRUCT_TRAITS_END()
-IPC_STRUCT_TRAITS_BEGIN(appcache::AppCacheErrorDetails)
+IPC_STRUCT_TRAITS_BEGIN(content::AppCacheErrorDetails)
IPC_STRUCT_TRAITS_MEMBER(message)
IPC_STRUCT_TRAITS_MEMBER(reason)
IPC_STRUCT_TRAITS_MEMBER(url)
@@ -96,7 +96,7 @@ IPC_MESSAGE_CONTROL3(AppCacheHostMsg_MarkAsForeignEntry,
// Returns the status of the appcache associated with host_id.
IPC_SYNC_MESSAGE_CONTROL1_1(AppCacheHostMsg_GetStatus,
int /* host_id */,
- appcache::AppCacheStatus)
+ content::AppCacheStatus)
// Initiates an update of the appcache associated with host_id.
IPC_SYNC_MESSAGE_CONTROL1_1(AppCacheHostMsg_StartUpdate,
@@ -111,7 +111,7 @@ IPC_SYNC_MESSAGE_CONTROL1_1(AppCacheHostMsg_SwapCache,
// Gets resource list from appcache synchronously.
IPC_SYNC_MESSAGE_CONTROL1_1(AppCacheHostMsg_GetResourceList,
int /* host_id in*/,
- std::vector<appcache::AppCacheResourceInfo>
+ std::vector<content::AppCacheResourceInfo>
/* resources out */)
@@ -121,18 +121,18 @@ IPC_SYNC_MESSAGE_CONTROL1_1(AppCacheHostMsg_GetResourceList,
// a particular host. This is sent in reply to AppCacheHostMsg_SelectCache.
IPC_MESSAGE_CONTROL2(AppCacheMsg_CacheSelected,
int /* host_id */,
- appcache::AppCacheInfo)
+ content::AppCacheInfo)
// Notifies the renderer of an AppCache status change.
IPC_MESSAGE_CONTROL2(AppCacheMsg_StatusChanged,
std::vector<int> /* host_ids */,
- appcache::AppCacheStatus)
+ content::AppCacheStatus)
// Notifies the renderer of an AppCache event other than the
// progress event which has a seperate message.
IPC_MESSAGE_CONTROL2(AppCacheMsg_EventRaised,
std::vector<int> /* host_ids */,
- appcache::AppCacheEventID)
+ content::AppCacheEventID)
// Notifies the renderer of an AppCache progress event.
IPC_MESSAGE_CONTROL4(AppCacheMsg_ProgressEventRaised,
@@ -144,7 +144,7 @@ IPC_MESSAGE_CONTROL4(AppCacheMsg_ProgressEventRaised,
// Notifies the renderer of an AppCache error event.
IPC_MESSAGE_CONTROL2(AppCacheMsg_ErrorEventRaised,
std::vector<int> /* host_ids */,
- appcache::AppCacheErrorDetails)
+ content::AppCacheErrorDetails)
// Notifies the renderer of an AppCache logging message.
IPC_MESSAGE_CONTROL3(AppCacheMsg_LogMessage,
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index 66db276..a0d87b8 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -55,6 +55,7 @@
'public/browser/android/synchronous_compositor.h',
'public/browser/android/ui_resource_client_android.h',
'public/browser/android/ui_resource_provider.h',
+ 'public/browser/appcache/appcache_service.h',
'public/browser/ax_event_notification_details.cc',
'public/browser/ax_event_notification_details.h',
'public/browser/blob_handle.h',
@@ -312,16 +313,54 @@
'browser/android/web_contents_observer_android.h',
'browser/android/ui_resource_provider_impl.cc',
'browser/android/ui_resource_provider_impl.h',
+ 'browser/appcache/appcache.cc',
+ 'browser/appcache/appcache.h',
+ 'browser/appcache/appcache_backend_impl.cc',
+ 'browser/appcache/appcache_backend_impl.h',
+ 'browser/appcache/appcache_database.cc',
+ 'browser/appcache/appcache_database.h',
+ 'browser/appcache/appcache_disk_cache.cc',
+ 'browser/appcache/appcache_disk_cache.h',
'browser/appcache/appcache_dispatcher_host.cc',
'browser/appcache/appcache_dispatcher_host.h',
+ 'browser/appcache/appcache_entry.h',
+ 'browser/appcache/appcache_executable_handler.h',
'browser/appcache/appcache_frontend_proxy.cc',
'browser/appcache/appcache_frontend_proxy.h',
+ 'browser/appcache/appcache_group.cc',
+ 'browser/appcache/appcache_group.h',
+ 'browser/appcache/appcache_histograms.cc',
+ 'browser/appcache/appcache_histograms.h',
+ 'browser/appcache/appcache_host.cc',
+ 'browser/appcache/appcache_host.h',
'browser/appcache/appcache_interceptor.cc',
'browser/appcache/appcache_interceptor.h',
+ 'browser/appcache/appcache_policy.h',
+ 'browser/appcache/appcache_quota_client.cc',
+ 'browser/appcache/appcache_quota_client.h',
+ 'browser/appcache/appcache_request_handler.cc',
+ 'browser/appcache/appcache_request_handler.h',
+ 'browser/appcache/appcache_response.cc',
+ 'browser/appcache/appcache_response.h',
+ 'browser/appcache/appcache_service_impl.cc',
+ 'browser/appcache/appcache_service_impl.h',
+ 'browser/appcache/appcache_storage.cc',
+ 'browser/appcache/appcache_storage.h',
+ 'browser/appcache/appcache_storage_impl.cc',
+ 'browser/appcache/appcache_storage_impl.h',
+ 'browser/appcache/appcache_update_job.cc',
+ 'browser/appcache/appcache_update_job.h',
+ 'browser/appcache/appcache_url_request_job.cc',
+ 'browser/appcache/appcache_url_request_job.h',
+ 'browser/appcache/appcache_working_set.cc',
+ 'browser/appcache/appcache_working_set.h',
'browser/appcache/chrome_appcache_service.cc',
'browser/appcache/chrome_appcache_service.h',
- 'browser/appcache/view_appcache_internals_job.h',
+ 'browser/appcache/manifest_parser.cc',
+ 'browser/appcache/manifest_parser.h',
'browser/appcache/view_appcache_internals_job.cc',
+ 'browser/appcache/view_appcache_internals_job.h',
+
'browser/battery_status/battery_status_manager_android.cc',
'browser/battery_status/battery_status_manager_default.cc',
'browser/battery_status/battery_status_manager.h',
@@ -1652,6 +1691,10 @@
'../media/media.gyp:media',
'content.gyp:content_jni_headers',
],
+ 'defines': ['APPCACHE_USE_SIMPLE_CACHE'],
+ 'direct_dependent_settings': {
+ 'defines': ['APPCACHE_USE_SIMPLE_CACHE'],
+ },
'link_settings': {
'libraries': [
'-ljnigraphics',
diff --git a/content/content_common.gypi b/content/content_common.gypi
index 7bc6b8ce..a67b53a 100644
--- a/content/content_common.gypi
+++ b/content/content_common.gypi
@@ -32,6 +32,7 @@
],
'variables': {
'public_common_sources': [
+ 'public/common/appcache_info.h',
'public/common/bindings_policy.h',
'public/common/child_process_host.h',
'public/common/child_process_host_delegate.cc',
@@ -146,6 +147,8 @@
'common/android/surface_texture_lookup.h',
'common/android/surface_texture_peer.cc',
'common/android/surface_texture_peer.h',
+ 'common/appcache_interfaces.cc',
+ 'common/appcache_interfaces.h',
'common/appcache_messages.h',
'common/battery_status_messages.h',
'common/browser_plugin/browser_plugin_constants.cc',
diff --git a/content/public/browser/appcache_service.h b/content/public/browser/appcache_service.h
new file mode 100644
index 0000000..7742abd
--- /dev/null
+++ b/content/public/browser/appcache_service.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2012 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.
+
+#ifndef CONTENT_PUBLIC_BROWSER_APPCACHE_SERVICE_H_
+#define CONTENT_PUBLIC_BROWSER_APPCACHE_SERVICE_H_
+
+#include <map>
+#include <set>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/common/content_export.h"
+#include "content/public/common/appcache_info.h"
+#include "net/base/completion_callback.h"
+
+namespace content {
+
+class AppCacheStorage;
+
+// Refcounted container to avoid copying the collection in callbacks.
+struct CONTENT_EXPORT AppCacheInfoCollection
+ : public base::RefCountedThreadSafe<AppCacheInfoCollection> {
+ AppCacheInfoCollection();
+
+ std::map<GURL, AppCacheInfoVector> infos_by_origin;
+
+ private:
+ friend class base::RefCountedThreadSafe<AppCacheInfoCollection>;
+ virtual ~AppCacheInfoCollection();
+};
+
+// Exposes a limited interface to the AppCacheService.
+// Call these methods only on the IO thread.
+class CONTENT_EXPORT AppCacheService {
+ public:
+ // Determines if a request for 'url' can be satisfied while offline.
+ // This method always completes asynchronously.
+ virtual void CanHandleMainResourceOffline(const GURL& url,
+ const GURL& first_party,
+ const net::CompletionCallback&
+ callback) = 0;
+
+ // Populates 'collection' with info about all of the appcaches stored
+ // within the service, 'callback' is invoked upon completion. The service
+ // acquires a reference to the 'collection' until until completion.
+ // This method always completes asynchronously.
+ virtual void GetAllAppCacheInfo(AppCacheInfoCollection* collection,
+ const net::CompletionCallback& callback) = 0;
+
+ // Deletes the group identified by 'manifest_url', 'callback' is
+ // invoked upon completion. Upon completion, the cache group and
+ // any resources within the group are no longer loadable and all
+ // subresource loads for pages associated with a deleted group
+ // will fail. This method always completes asynchronously.
+ virtual void DeleteAppCacheGroup(const GURL& manifest_url,
+ const net::CompletionCallback& callback) = 0;
+
+ protected:
+ virtual ~AppCacheService() {}
+};
+
+} // namespace content
+
+#endif // CONTENT_PUBLIC_BROWSER_APPCACHE_SERVICE_H_
diff --git a/content/public/browser/resource_context.h b/content/public/browser/resource_context.h
index 58eb298..1c21de0 100644
--- a/content/public/browser/resource_context.h
+++ b/content/public/browser/resource_context.h
@@ -16,7 +16,7 @@
class GURL;
-namespace appcache {
+namespace content {
class AppCacheService;
}
diff --git a/content/public/browser/resource_dispatcher_host_delegate.cc b/content/public/browser/resource_dispatcher_host_delegate.cc
index b7b96c3..ceed430 100644
--- a/content/public/browser/resource_dispatcher_host_delegate.cc
+++ b/content/public/browser/resource_dispatcher_host_delegate.cc
@@ -21,7 +21,7 @@ bool ResourceDispatcherHostDelegate::ShouldBeginRequest(
void ResourceDispatcherHostDelegate::RequestBeginning(
net::URLRequest* request,
ResourceContext* resource_context,
- appcache::AppCacheService* appcache_service,
+ AppCacheService* appcache_service,
ResourceType::Type resource_type,
int child_id,
int route_id,
diff --git a/content/public/browser/resource_dispatcher_host_delegate.h b/content/public/browser/resource_dispatcher_host_delegate.h
index 45796ba..d16a11f 100644
--- a/content/public/browser/resource_dispatcher_host_delegate.h
+++ b/content/public/browser/resource_dispatcher_host_delegate.h
@@ -15,7 +15,7 @@
class GURL;
template <class T> class ScopedVector;
-namespace appcache {
+namespace content {
class AppCacheService;
}
@@ -58,7 +58,7 @@ class CONTENT_EXPORT ResourceDispatcherHostDelegate {
virtual void RequestBeginning(
net::URLRequest* request,
ResourceContext* resource_context,
- appcache::AppCacheService* appcache_service,
+ AppCacheService* appcache_service,
ResourceType::Type resource_type,
int child_id,
int route_id,
diff --git a/content/public/browser/storage_partition.h b/content/public/browser/storage_partition.h
index 3a4139d..ea8baa3 100644
--- a/content/public/browser/storage_partition.h
+++ b/content/public/browser/storage_partition.h
@@ -12,7 +12,7 @@
class GURL;
-namespace appcache {
+namespace content {
class AppCacheService;
}
@@ -52,7 +52,7 @@ class StoragePartition {
virtual net::URLRequestContextGetter* GetURLRequestContext() = 0;
virtual net::URLRequestContextGetter* GetMediaURLRequestContext() = 0;
virtual quota::QuotaManager* GetQuotaManager() = 0;
- virtual appcache::AppCacheService* GetAppCacheService() = 0;
+ virtual AppCacheService* GetAppCacheService() = 0;
virtual fileapi::FileSystemContext* GetFileSystemContext() = 0;
virtual webkit_database::DatabaseTracker* GetDatabaseTracker() = 0;
virtual DOMStorageContext* GetDOMStorageContext() = 0;
diff --git a/content/public/common/appcache_info.h b/content/public/common/appcache_info.h
new file mode 100644
index 0000000..f37e015
--- /dev/null
+++ b/content/public/common/appcache_info.h
@@ -0,0 +1,50 @@
+// Copyright (c) 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.
+
+#ifndef CONTENT_PUBLIC_COMMON_APPCACHE_INFO_H_
+#define CONTENT_PUBLIC_COMMON_APPCACHE_INFO_H_
+
+#include <vector>
+
+#include "base/time/time.h"
+#include "content/common/content_export.h"
+#include "url/gurl.h"
+
+namespace content {
+
+static const int kAppCacheNoHostId = 0;
+static const int64 kAppCacheNoCacheId = 0;
+static const int64 kAppCacheNoResponseId = 0;
+static const int64 kAppCacheUnknownCacheId = -1;
+
+enum AppCacheStatus {
+ APPCACHE_STATUS_UNCACHED,
+ APPCACHE_STATUS_IDLE,
+ APPCACHE_STATUS_CHECKING,
+ APPCACHE_STATUS_DOWNLOADING,
+ APPCACHE_STATUS_UPDATE_READY,
+ APPCACHE_STATUS_OBSOLETE,
+ APPCACHE_STATUS_LAST = APPCACHE_STATUS_OBSOLETE
+};
+
+struct CONTENT_EXPORT AppCacheInfo {
+ AppCacheInfo();
+ ~AppCacheInfo();
+
+ GURL manifest_url;
+ base::Time creation_time;
+ base::Time last_update_time;
+ base::Time last_access_time;
+ int64 cache_id;
+ int64 group_id;
+ AppCacheStatus status;
+ int64 size;
+ bool is_complete;
+};
+
+typedef std::vector<AppCacheInfo> AppCacheInfoVector;
+
+} // namespace
+
+#endif // CONTENT_PUBLIC_COMMON_APPCACHE_INFO_H_
diff --git a/content/public/common/resource_response_info.cc b/content/public/common/resource_response_info.cc
index 70dbc50..e827f3e 100644
--- a/content/public/common/resource_response_info.cc
+++ b/content/public/common/resource_response_info.cc
@@ -4,15 +4,15 @@
#include "content/public/common/resource_response_info.h"
+#include "content/public/common/appcache_info.h"
#include "net/http/http_response_headers.h"
-#include "webkit/common/appcache/appcache_interfaces.h"
namespace content {
ResourceResponseInfo::ResourceResponseInfo()
: content_length(-1),
encoded_data_length(-1),
- appcache_id(appcache::kAppCacheNoCacheId),
+ appcache_id(kAppCacheNoCacheId),
was_fetched_via_spdy(false),
was_npn_negotiated(false),
was_alternate_protocol_available(false),
diff --git a/content/renderer/renderer_webapplicationcachehost_impl.cc b/content/renderer/renderer_webapplicationcachehost_impl.cc
index 5b781d4..42aae61 100644
--- a/content/renderer/renderer_webapplicationcachehost_impl.cc
+++ b/content/renderer/renderer_webapplicationcachehost_impl.cc
@@ -10,7 +10,6 @@
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebView.h"
-using appcache::AppCacheBackend;
using blink::WebApplicationCacheHostClient;
using blink::WebConsoleMessage;
@@ -25,7 +24,7 @@ RendererWebApplicationCacheHostImpl::RendererWebApplicationCacheHostImpl(
}
void RendererWebApplicationCacheHostImpl::OnLogMessage(
- appcache::AppCacheLogLevel log_level, const std::string& message) {
+ AppCacheLogLevel log_level, const std::string& message) {
if (RenderThreadImpl::current()->layout_test_mode())
return;
@@ -47,7 +46,7 @@ void RendererWebApplicationCacheHostImpl::OnContentBlocked(
}
void RendererWebApplicationCacheHostImpl::OnCacheSelected(
- const appcache::AppCacheInfo& info) {
+ const AppCacheInfo& info) {
if (!info.manifest_url.is_empty()) {
RenderThreadImpl::current()->Send(new ViewHostMsg_AppCacheAccessed(
routing_id_, info.manifest_url, false));
diff --git a/content/renderer/renderer_webapplicationcachehost_impl.h b/content/renderer/renderer_webapplicationcachehost_impl.h
index e7b2bc8..a752c08 100644
--- a/content/renderer/renderer_webapplicationcachehost_impl.h
+++ b/content/renderer/renderer_webapplicationcachehost_impl.h
@@ -15,13 +15,13 @@ class RendererWebApplicationCacheHostImpl : public WebApplicationCacheHostImpl {
RendererWebApplicationCacheHostImpl(
RenderViewImpl* render_view,
blink::WebApplicationCacheHostClient* client,
- appcache::AppCacheBackend* backend);
+ AppCacheBackend* backend);
// WebApplicationCacheHostImpl:
- virtual void OnLogMessage(appcache::AppCacheLogLevel log_level,
+ virtual void OnLogMessage(AppCacheLogLevel log_level,
const std::string& message) OVERRIDE;
virtual void OnContentBlocked(const GURL& manifest_url) OVERRIDE;
- virtual void OnCacheSelected(const appcache::AppCacheInfo& info) OVERRIDE;
+ virtual void OnCacheSelected(const AppCacheInfo& info) OVERRIDE;
private:
RenderViewImpl* GetRenderView();
diff --git a/content/test/appcache_test_helper.cc b/content/test/appcache_test_helper.cc
index 087c721..1a95c05 100644
--- a/content/test/appcache_test_helper.cc
+++ b/content/test/appcache_test_helper.cc
@@ -7,11 +7,11 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/message_loop/message_loop.h"
+#include "content/browser/appcache/appcache.h"
+#include "content/browser/appcache/appcache_entry.h"
+#include "content/browser/appcache/appcache_group.h"
+#include "content/browser/appcache/appcache_service_impl.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/browser/appcache/appcache.h"
-#include "webkit/browser/appcache/appcache_entry.h"
-#include "webkit/browser/appcache/appcache_group.h"
-#include "webkit/browser/appcache/appcache_service_impl.h"
namespace content {
@@ -24,23 +24,23 @@ AppCacheTestHelper::AppCacheTestHelper()
AppCacheTestHelper::~AppCacheTestHelper() {}
void AppCacheTestHelper::OnGroupAndNewestCacheStored(
- appcache::AppCacheGroup* /*group*/,
- appcache::AppCache* /*newest_cache*/,
+ AppCacheGroup* /*group*/,
+ AppCache* /*newest_cache*/,
bool success,
bool /*would_exceed_quota*/) {
ASSERT_TRUE(success);
base::MessageLoop::current()->Quit();
}
-void AppCacheTestHelper::AddGroupAndCache(appcache::AppCacheServiceImpl*
+void AppCacheTestHelper::AddGroupAndCache(AppCacheServiceImpl*
appcache_service, const GURL& manifest_url) {
- appcache::AppCacheGroup* appcache_group =
- new appcache::AppCacheGroup(appcache_service->storage(),
+ AppCacheGroup* appcache_group =
+ new AppCacheGroup(appcache_service->storage(),
manifest_url,
++group_id_);
- appcache::AppCache* appcache = new appcache::AppCache(
+ AppCache* appcache = new AppCache(
appcache_service->storage(), ++appcache_id_);
- appcache::AppCacheEntry entry(appcache::AppCacheEntry::MANIFEST,
+ AppCacheEntry entry(AppCacheEntry::MANIFEST,
++response_id_);
appcache->AddEntry(manifest_url, entry);
appcache->set_complete(true);
@@ -52,9 +52,9 @@ void AppCacheTestHelper::AddGroupAndCache(appcache::AppCacheServiceImpl*
base::MessageLoop::current()->Run();
}
-void AppCacheTestHelper::GetOriginsWithCaches(appcache::AppCacheServiceImpl*
+void AppCacheTestHelper::GetOriginsWithCaches(AppCacheServiceImpl*
appcache_service, std::set<GURL>* origins) {
- appcache_info_ = new appcache::AppCacheInfoCollection;
+ appcache_info_ = new AppCacheInfoCollection;
origins_ = origins;
appcache_service->GetAllAppCacheInfo(
appcache_info_.get(),
@@ -66,7 +66,7 @@ void AppCacheTestHelper::GetOriginsWithCaches(appcache::AppCacheServiceImpl*
}
void AppCacheTestHelper::OnGotAppCacheInfo(int rv) {
- typedef std::map<GURL, appcache::AppCacheInfoVector> InfoByOrigin;
+ typedef std::map<GURL, AppCacheInfoVector> InfoByOrigin;
origins_->clear();
for (InfoByOrigin::const_iterator origin =
diff --git a/content/test/appcache_test_helper.h b/content/test/appcache_test_helper.h
index f93424b..3b6f6c9 100644
--- a/content/test/appcache_test_helper.h
+++ b/content/test/appcache_test_helper.h
@@ -7,9 +7,9 @@
#include <set>
-#include "webkit/browser/appcache/appcache_storage.h"
+#include "content/browser/appcache/appcache_storage.h"
-namespace appcache {
+namespace content {
class AppCacheServiceImpl;
}
@@ -17,19 +17,19 @@ namespace content {
// Helper class for inserting data into a ChromeAppCacheService and reading it
// back.
-class AppCacheTestHelper : public appcache::AppCacheStorage::Delegate {
+class AppCacheTestHelper : public AppCacheStorage::Delegate {
public:
AppCacheTestHelper();
virtual ~AppCacheTestHelper();
- void AddGroupAndCache(appcache::AppCacheServiceImpl* appcache_service,
+ void AddGroupAndCache(AppCacheServiceImpl* appcache_service,
const GURL& manifest_url);
- void GetOriginsWithCaches(appcache::AppCacheServiceImpl* appcache_service,
+ void GetOriginsWithCaches(AppCacheServiceImpl* appcache_service,
std::set<GURL>* origins);
private:
virtual void OnGroupAndNewestCacheStored(
- appcache::AppCacheGroup* group,
- appcache::AppCache* newest_cache,
+ AppCacheGroup* group,
+ AppCache* newest_cache,
bool success,
bool would_exceed_quota) OVERRIDE;
void OnGotAppCacheInfo(int rv);
@@ -37,7 +37,7 @@ class AppCacheTestHelper : public appcache::AppCacheStorage::Delegate {
int group_id_;
int appcache_id_;
int response_id_;
- scoped_refptr<appcache::AppCacheInfoCollection> appcache_info_;
+ scoped_refptr<AppCacheInfoCollection> appcache_info_;
std::set<GURL>* origins_; // not owned
DISALLOW_COPY_AND_ASSIGN(AppCacheTestHelper);