summaryrefslogtreecommitdiffstats
path: root/webkit/appcache
diff options
context:
space:
mode:
authormichaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-15 22:07:15 +0000
committermichaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-15 22:07:15 +0000
commit97e3edc23314c476859c22c8db601f9b3dec552d (patch)
tree9c589f47545b88c1453c2481a7844a921636485c /webkit/appcache
parent34fce2a5030cb53a1e2decb37b5f7517f98457f7 (diff)
downloadchromium_src-97e3edc23314c476859c22c8db601f9b3dec552d.zip
chromium_src-97e3edc23314c476859c22c8db601f9b3dec552d.tar.gz
chromium_src-97e3edc23314c476859c22c8db601f9b3dec552d.tar.bz2
* Fleshed out AppCacheHost class a fair amount, in particular the cache selection algorithm.
* Added some AppCacheHost unit tests. * Introduced AppCacheRequestHandler class, which replaces the clunkyApp CacheInterceptor::ExtraInfo struct. This impl is entirely skeletal stubs for now. TEST=appcache_unittest.cc, but really needs more BUG=none Review URL: http://codereview.chromium.org/192043 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@26275 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/appcache')
-rw-r--r--webkit/appcache/appcache.h16
-rw-r--r--webkit/appcache/appcache_backend_impl.cc73
-rw-r--r--webkit/appcache/appcache_backend_impl.h56
-rw-r--r--webkit/appcache/appcache_entry.h12
-rw-r--r--webkit/appcache/appcache_frontend_impl.h2
-rw-r--r--webkit/appcache/appcache_group.cc8
-rw-r--r--webkit/appcache/appcache_group.h17
-rw-r--r--webkit/appcache/appcache_group_unittest.cc67
-rw-r--r--webkit/appcache/appcache_host.cc247
-rw-r--r--webkit/appcache/appcache_host.h114
-rw-r--r--webkit/appcache/appcache_host_unittest.cc230
-rw-r--r--webkit/appcache/appcache_interceptor.cc116
-rw-r--r--webkit/appcache/appcache_interceptor.h8
-rw-r--r--webkit/appcache/appcache_request_handler.cc63
-rw-r--r--webkit/appcache/appcache_request_handler.h53
-rw-r--r--webkit/appcache/appcache_service.cc37
-rw-r--r--webkit/appcache/appcache_service.h46
-rw-r--r--webkit/appcache/appcache_service_unittest.cc12
-rw-r--r--webkit/appcache/appcache_unittest.cc27
-rw-r--r--webkit/appcache/manifest_parser_unittest.cc11
-rw-r--r--webkit/appcache/web_application_cache_host_impl.cc5
21 files changed, 988 insertions, 232 deletions
diff --git a/webkit/appcache/appcache.h b/webkit/appcache/appcache.h
index 15944b6..fcfa9d1 100644
--- a/webkit/appcache/appcache.h
+++ b/webkit/appcache/appcache.h
@@ -30,14 +30,16 @@ class AppCache : public base::RefCounted<AppCache> {
AppCache(AppCacheService *service, int64 cache_id);
~AppCache();
- int64 cache_id() { return cache_id_; }
+ int64 cache_id() const { return cache_id_; }
- AppCacheGroup* owning_group() { return owning_group_; }
+ AppCacheGroup* owning_group() const { return owning_group_; }
void set_owning_group(AppCacheGroup* group) { owning_group_ = group; }
- bool is_complete() { return is_complete_; }
+ bool is_complete() const { return is_complete_; }
void set_complete(bool value) { is_complete_ = value; }
+ AppCacheService* service() const { return service_; }
+
// Adds a new entry. Entry must not already be in cache.
void AddEntry(const GURL& url, const AppCacheEntry& entry);
@@ -49,9 +51,9 @@ class AppCache : public base::RefCounted<AppCache> {
AppCacheEntry* GetEntry(const GURL& url);
typedef std::map<GURL, AppCacheEntry> EntryMap;
- const EntryMap& entries() { return entries_; }
+ const EntryMap& entries() const { return entries_; }
- bool IsNewerThan(AppCache* cache) {
+ bool IsNewerThan(AppCache* cache) const {
return update_time_ > cache->update_time_;
}
@@ -62,13 +64,13 @@ class AppCache : public base::RefCounted<AppCache> {
private:
friend class AppCacheHost;
- // Use AppCacheHost::set_selected_cache() to manipulate host association.
+ // Use AppCacheHost::AssociateCache() to manipulate host association.
void AssociateHost(AppCacheHost* host) {
associated_hosts_.insert(host);
}
void UnassociateHost(AppCacheHost* host);
- int64 cache_id_;
+ const int64 cache_id_;
AppCacheEntry* manifest_; // also in entry map
AppCacheGroup* owning_group_;
std::set<AppCacheHost*> associated_hosts_;
diff --git a/webkit/appcache/appcache_backend_impl.cc b/webkit/appcache/appcache_backend_impl.cc
index 22e4e0f..f7788ae 100644
--- a/webkit/appcache/appcache_backend_impl.cc
+++ b/webkit/appcache/appcache_backend_impl.cc
@@ -4,7 +4,7 @@
#include "webkit/appcache/appcache_backend_impl.h"
-#include "webkit/appcache/appcache_host.h"
+#include "base/stl_util-inl.h"
#include "webkit/appcache/appcache.h"
#include "webkit/appcache/appcache_group.h"
#include "webkit/appcache/appcache_service.h"
@@ -13,6 +13,7 @@
namespace appcache {
AppCacheBackendImpl::~AppCacheBackendImpl() {
+ STLDeleteValues(&hosts_);
if (service_)
service_->UnregisterBackend(this);
}
@@ -27,47 +28,79 @@ void AppCacheBackendImpl::Initialize(AppCacheService* service,
service_->RegisterBackend(this);
}
-void AppCacheBackendImpl::RegisterHost(int id) {
- DCHECK(hosts_.find(id) == hosts_.end());
- hosts_.insert(HostMap::value_type(id, AppCacheHost(id, frontend_)));
+bool AppCacheBackendImpl::RegisterHost(int id) {
+ if (GetHost(id))
+ return false;
+
+ hosts_.insert(
+ HostMap::value_type(id, new AppCacheHost(id, frontend_, service_)));
+ return true;
}
-void AppCacheBackendImpl::UnregisterHost(int id) {
- hosts_.erase(id);
+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;
}
-void AppCacheBackendImpl::SelectCache(
+bool AppCacheBackendImpl::SelectCache(
int host_id,
const GURL& document_url,
const int64 cache_document_was_loaded_from,
const GURL& manifest_url) {
- // TODO(michaeln): write me
- frontend_->OnCacheSelected(host_id, kNoCacheId, UNCACHED);
+ AppCacheHost* host = GetHost(host_id);
+ if (!host)
+ return false;
+
+ host->SelectCache(document_url, cache_document_was_loaded_from,
+ manifest_url);
+ return true;
}
-void AppCacheBackendImpl::MarkAsForeignEntry(
+bool AppCacheBackendImpl::MarkAsForeignEntry(
int host_id,
const GURL& document_url,
int64 cache_document_was_loaded_from) {
- // TODO(michaeln): write me
+ AppCacheHost* host = GetHost(host_id);
+ if (!host)
+ return false;
+
+ host->MarkAsForeignEntry(document_url, cache_document_was_loaded_from);
+ return true;
}
-void AppCacheBackendImpl::GetStatusWithCallback(
+bool AppCacheBackendImpl::GetStatusWithCallback(
int host_id, GetStatusCallback* callback, void* callback_param) {
- // TODO(michaeln): write me
- callback->Run(UNCACHED, callback_param);
+ AppCacheHost* host = GetHost(host_id);
+ if (!host)
+ return false;
+
+ host->GetStatusWithCallback(callback, callback_param);
+ return true;
}
-void AppCacheBackendImpl::StartUpdateWithCallback(
+bool AppCacheBackendImpl::StartUpdateWithCallback(
int host_id, StartUpdateCallback* callback, void* callback_param) {
- // TODO(michaeln): write me
- callback->Run(false, callback_param);
+ AppCacheHost* host = GetHost(host_id);
+ if (!host)
+ return false;
+
+ host->StartUpdateWithCallback(callback, callback_param);
+ return true;
}
-void AppCacheBackendImpl::SwapCacheWithCallback(
+bool AppCacheBackendImpl::SwapCacheWithCallback(
int host_id, SwapCacheCallback* callback, void* callback_param) {
- // TODO(michaeln): write me
- callback->Run(false, callback_param);
+ AppCacheHost* host = GetHost(host_id);
+ if (!host)
+ return false;
+
+ host->SwapCacheWithCallback(callback, callback_param);
+ return true;
}
} // namespace appcache
diff --git a/webkit/appcache/appcache_backend_impl.h b/webkit/appcache/appcache_backend_impl.h
index abf4247..4ae4e49 100644
--- a/webkit/appcache/appcache_backend_impl.h
+++ b/webkit/appcache/appcache_backend_impl.h
@@ -5,22 +5,15 @@
#ifndef WEBKIT_APPCACHE_APPCACHE_BACKEND_IMPL_H_
#define WEBKIT_APPCACHE_APPCACHE_BACKEND_IMPL_H_
-#include <map>
-
#include "base/logging.h"
-#include "base/task.h"
+#include "base/hash_tables.h"
#include "webkit/appcache/appcache_host.h"
-#include "webkit/appcache/appcache_interfaces.h"
namespace appcache {
class AppCacheService;
-typedef Callback2<Status, void*>::Type GetStatusCallback;
-typedef Callback2<bool, void*>::Type StartUpdateCallback;
-typedef Callback2<bool, void*>::Type SwapCacheCallback;
-
-class AppCacheBackendImpl : public AppCacheBackend {
+class AppCacheBackendImpl {
public:
AppCacheBackendImpl() : service_(NULL), frontend_(NULL), process_id_(0) {}
~AppCacheBackendImpl();
@@ -31,38 +24,33 @@ class AppCacheBackendImpl : public AppCacheBackend {
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 SelectCache(int host_id,
+ const GURL& document_url,
+ const int64 cache_document_was_loaded_from,
+ const GURL& manifest_url);
+ bool MarkAsForeignEntry(int host_id, const GURL& document_url,
+ int64 cache_document_was_loaded_from);
+ bool GetStatusWithCallback(int host_id, GetStatusCallback* callback,
+ void* callback_param);
+ bool StartUpdateWithCallback(int host_id, StartUpdateCallback* callback,
+ void* callback_param);
+ bool SwapCacheWithCallback(int host_id, 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;
+ return (it != hosts_.end()) ? (it->second) : NULL;
}
- typedef std::map<int, AppCacheHost> HostMap;
+ typedef base::hash_map<int, AppCacheHost*> HostMap;
const HostMap& hosts() { return hosts_; }
- // AppCacheBackend methods
- virtual void RegisterHost(int host_id);
- virtual void UnregisterHost(int host_id);
- virtual void SelectCache(int host_id,
- const GURL& document_url,
- const int64 cache_document_was_loaded_from,
- const GURL& manifest_url);
- virtual void MarkAsForeignEntry(int host_id, const GURL& document_url,
- int64 cache_document_was_loaded_from);
-
- // We don't use the sync variants in the backend, would block the IO thread.
- virtual Status GetStatus(int host_id) { NOTREACHED(); return UNCACHED; }
- virtual bool StartUpdate(int host_id) { NOTREACHED(); return false; }
- virtual bool SwapCache(int host_id) { NOTREACHED(); return false; }
-
- // Async variants of the sync methods defined in the backend interface.
- void GetStatusWithCallback(int host_id, GetStatusCallback* callback,
- void* callback_param);
- void StartUpdateWithCallback(int host_id, StartUpdateCallback* callback,
- void* callback_param);
- void SwapCacheWithCallback(int host_id, SwapCacheCallback* callback,
- void* callback_param);
-
private:
AppCacheService* service_;
AppCacheFrontend* frontend_;
diff --git a/webkit/appcache/appcache_entry.h b/webkit/appcache/appcache_entry.h
index 5629cc2..434568a 100644
--- a/webkit/appcache/appcache_entry.h
+++ b/webkit/appcache/appcache_entry.h
@@ -5,8 +5,6 @@
#ifndef WEBKIT_APPCACHE_APPCACHE_ENTRY_H_
#define WEBKIT_APPCACHE_APPCACHE_ENTRY_H_
-#include "googleurl/src/gurl.h"
-
namespace appcache {
// A cached entry is identified by a URL and is classified into one
@@ -28,11 +26,11 @@ class AppCacheEntry {
int types() const { return types_; }
void add_types(int added_types) { types_ |= added_types; }
- bool IsMaster() const { return types_ & MASTER; }
- bool IsManifest() const { return types_ & MANIFEST; }
- bool IsExplicit() const { return types_ & EXPLICIT; }
- bool IsForeign() const { return types_ & FOREIGN; }
- bool IsFallback() const { return types_ & FALLBACK; }
+ 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; }
private:
int types_;
diff --git a/webkit/appcache/appcache_frontend_impl.h b/webkit/appcache/appcache_frontend_impl.h
index e4ed922..cc10c27 100644
--- a/webkit/appcache/appcache_frontend_impl.h
+++ b/webkit/appcache/appcache_frontend_impl.h
@@ -12,7 +12,7 @@ namespace appcache {
class AppCacheFrontendImpl : public AppCacheFrontend {
public:
- virtual void OnCacheSelected(int host_id, int64 cache_id ,
+ virtual void OnCacheSelected(int host_id, int64 cache_id,
Status status);
virtual void OnStatusChanged(const std::vector<int>& host_ids,
Status status);
diff --git a/webkit/appcache/appcache_group.cc b/webkit/appcache/appcache_group.cc
index 4299a67..10307c2 100644
--- a/webkit/appcache/appcache_group.cc
+++ b/webkit/appcache/appcache_group.cc
@@ -8,6 +8,7 @@
#include "base/logging.h"
#include "webkit/appcache/appcache.h"
+#include "webkit/appcache/appcache_host.h"
#include "webkit/appcache/appcache_service.h"
namespace appcache {
@@ -61,7 +62,7 @@ bool AppCacheGroup::RemoveCache(AppCache* cache) {
} else {
// Unused old cache can always be removed.
Caches::iterator it =
- std::find(old_caches_.begin(), old_caches_.end(), cache);
+ std::find(old_caches_.begin(), old_caches_.end(), cache);
if (it != old_caches_.end()) {
(*it)->set_owning_group(NULL);
old_caches_.erase(it);
@@ -71,4 +72,9 @@ bool AppCacheGroup::RemoveCache(AppCache* cache) {
return true;
}
+void AppCacheGroup::StartUpdateWithNewMasterEntry(
+ AppCacheHost* host, const GURL& master_entry_url) {
+ // TODO(michaeln): use the real AppCacheUpdateJob
+}
+
} // namespace appcache
diff --git a/webkit/appcache/appcache_group.h b/webkit/appcache/appcache_group.h
index 49ba788..20422ae 100644
--- a/webkit/appcache/appcache_group.h
+++ b/webkit/appcache/appcache_group.h
@@ -13,6 +13,7 @@
namespace appcache {
class AppCache;
+class AppCacheHost;
class AppCacheService;
// Collection of application caches identified by the same manifest URL.
@@ -45,6 +46,22 @@ class AppCacheGroup : public base::RefCounted<AppCacheGroup> {
// cannot be removed as long as the group is still in use.
bool RemoveCache(AppCache* cache);
+ // 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::EmptyGURL());
+ }
+
+ // 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);
+
private:
GURL manifest_url_;
UpdateStatus update_status_;
diff --git a/webkit/appcache/appcache_group_unittest.cc b/webkit/appcache/appcache_group_unittest.cc
index c4e0cd1..bcfbce0 100644
--- a/webkit/appcache/appcache_group_unittest.cc
+++ b/webkit/appcache/appcache_group_unittest.cc
@@ -4,26 +4,50 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "webkit/appcache/appcache.h"
-#include "webkit/appcache/appcache_host.h"
#include "webkit/appcache/appcache_group.h"
+#include "webkit/appcache/appcache_host.h"
#include "webkit/appcache/appcache_service.h"
-using appcache::AppCache;
-using appcache::AppCacheHost;
-using appcache::AppCacheGroup;
-using appcache::AppCacheService;
-
namespace {
-class AppCacheGroupTest : public testing::Test {
+class TestAppCacheFrontend : public appcache::AppCacheFrontend {
+ public:
+ TestAppCacheFrontend()
+ : last_host_id_(-1), last_cache_id_(-1),
+ last_status_(appcache::OBSOLETE) {
+ }
+
+ virtual void OnCacheSelected(int host_id, int64 cache_id ,
+ appcache::Status status) {
+ last_host_id_ = host_id;
+ last_cache_id_ = cache_id;
+ last_status_ = status;
+ }
+
+ virtual void OnStatusChanged(const std::vector<int>& host_ids,
+ appcache::Status status) {
+ }
+
+ virtual void OnEventRaised(const std::vector<int>& host_ids,
+ appcache::EventID event_id) {
+ }
+
+ int last_host_id_;
+ int64 last_cache_id_;
+ appcache::Status last_status_;
};
-} // namespace
+} // namespace anon
+
+namespace appcache {
+
+class AppCacheGroupTest : public testing::Test {
+};
TEST(AppCacheGroupTest, AddRemoveCache) {
AppCacheService service;
scoped_refptr<AppCacheGroup> group =
- new AppCacheGroup(&service, GURL::EmptyGURL());
+ new AppCacheGroup(&service, GURL::EmptyGURL());
base::TimeTicks ticks = base::TimeTicks::Now();
@@ -66,10 +90,11 @@ TEST(AppCacheGroupTest, AddRemoveCache) {
TEST(AppCacheGroupTest, CleanupUnusedGroup) {
AppCacheService service;
+ TestAppCacheFrontend frontend;
AppCacheGroup* group = new AppCacheGroup(&service, GURL::EmptyGURL());
- AppCacheHost host1(1, NULL);
- AppCacheHost host2(2, NULL);
+ AppCacheHost host1(1, &frontend, &service);
+ AppCacheHost host2(2, &frontend, &service);
base::TimeTicks ticks = base::TimeTicks::Now();
@@ -80,8 +105,15 @@ TEST(AppCacheGroupTest, CleanupUnusedGroup) {
group->AddCache(cache1);
EXPECT_EQ(cache1, group->newest_complete_cache());
- host1.set_selected_cache(cache1);
- host2.set_selected_cache(cache1);
+ host1.AssociateCache(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::IDLE);
+
+ host2.AssociateCache(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::IDLE);
AppCache* cache2 = new AppCache(&service, 222);
cache2->set_complete(true);
@@ -91,6 +123,11 @@ TEST(AppCacheGroupTest, CleanupUnusedGroup) {
EXPECT_EQ(cache2, group->newest_complete_cache());
// Unassociate all hosts from older cache.
- host1.set_selected_cache(NULL);
- host2.set_selected_cache(NULL);
+ host1.AssociateCache(NULL);
+ host2.AssociateCache(NULL);
+ EXPECT_EQ(frontend.last_host_id_, host2.host_id());
+ EXPECT_EQ(frontend.last_cache_id_, appcache::kNoCacheId);
+ EXPECT_EQ(frontend.last_status_, appcache::UNCACHED);
}
+
+} // namespace appcache
diff --git a/webkit/appcache/appcache_host.cc b/webkit/appcache/appcache_host.cc
index 99f0544..252c618 100644
--- a/webkit/appcache/appcache_host.cc
+++ b/webkit/appcache/appcache_host.cc
@@ -8,33 +8,254 @@
#include "webkit/appcache/appcache.h"
#include "webkit/appcache/appcache_group.h"
#include "webkit/appcache/appcache_interfaces.h"
+#include "webkit/appcache/appcache_request_handler.h"
+#include "webkit/appcache/appcache_service.h"
namespace appcache {
-AppCacheHost::AppCacheHost(int host_id, AppCacheFrontend* frontend)
- : host_id_(host_id),
- selected_cache_(NULL),
- group_(NULL),
- frontend_(frontend) {
+AppCacheHost::AppCacheHost(int host_id, AppCacheFrontend* frontend,
+ AppCacheService* service)
+ : host_id_(host_id), pending_selected_cache_id_(kNoCacheId),
+ frontend_(frontend), service_(service),
+ pending_get_status_callback_(NULL), pending_start_update_callback_(NULL),
+ pending_swap_cache_callback_(NULL), pending_callback_param_(NULL) {
}
AppCacheHost::~AppCacheHost() {
- if (selected_cache_)
- set_selected_cache(NULL);
- DCHECK(!group_);
+ if (associated_cache_.get())
+ associated_cache_->UnassociateHost(this);
+ service_->CancelLoads(this);
}
-void AppCacheHost::set_selected_cache(AppCache *cache) {
- if (selected_cache_)
- selected_cache_->UnassociateHost(this);
+void AppCacheHost::SelectCache(const GURL& document_url,
+ const int64 cache_document_was_loaded_from,
+ const GURL& manifest_url) {
+ DCHECK(!pending_start_update_callback_ &&
+ !pending_swap_cache_callback_ &&
+ !pending_get_status_callback_);
- selected_cache_ = cache;
+ // First we handle an unusual case of SelectCache being called a second
+ // time. Generally this shouldn't happen, but with bad content I think
+ // this can occur... <html manifest=foo> <html manifest=bar></html></html>
+ // We handle this by killing whatever loading we have initiated, and by
+ // unassociating any hosts we currently have associated... and starting
+ // anew with the inputs to this SelectCache call.
+ // TODO(michaeln): at some point determine what behavior the algorithms
+ // described in the HTML5 draft produce and have our impl produce those
+ // results (or suggest changes to the algorihtms described in the spec
+ // if the resulting behavior is just too insane).
+ if (is_selection_pending()) {
+ service_->CancelLoads(this);
+ pending_selected_manifest_url_ = GURL::EmptyGURL();
+ pending_selected_cache_id_ = kNoCacheId;
+ } else if (associated_cache()) {
+ AssociateCache(NULL);
+ }
+ new_master_entry_url_ = GURL::EmptyGURL();
+
+ // 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: foriegn entries are detected on the client side and
+ // MarkAsForeignEntry is called in that case, so that detection
+ // step is skipped here.
+
+ if (cache_document_was_loaded_from != kNoCacheId) {
+ LoadCache(cache_document_was_loaded_from);
+ return;
+ }
+
+ if (!manifest_url.is_empty() &&
+ (manifest_url.GetOrigin() == document_url.GetOrigin())) {
+ 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::MarkAsForeignEntry(const GURL& document_url,
+ int64 cache_document_was_loaded_from) {
+ service_->MarkAsForeignEntry(document_url, cache_document_was_loaded_from);
+ SelectCache(document_url, kNoCacheId, GURL::EmptyGURL());
+}
+
+void AppCacheHost::GetStatusWithCallback(GetStatusCallback* callback,
+ void* callback_param) {
+ DCHECK(!pending_start_update_callback_ &&
+ !pending_swap_cache_callback_ &&
+ !pending_get_status_callback_);
+
+ pending_get_status_callback_ = callback;
+ pending_callback_param_ = callback_param;
+ if (is_selection_pending())
+ return;
+
+ DoPendingGetStatus();
+}
+
+void AppCacheHost::DoPendingGetStatus() {
+ DCHECK(pending_get_status_callback_);
+
+ pending_get_status_callback_->Run(
+ GetStatus(), pending_callback_param_);
+
+ pending_get_status_callback_ = NULL;
+ pending_callback_param_ = NULL;
+}
+
+void AppCacheHost::StartUpdateWithCallback(StartUpdateCallback* callback,
+ void* callback_param) {
+ DCHECK(!pending_start_update_callback_ &&
+ !pending_swap_cache_callback_ &&
+ !pending_get_status_callback_);
+
+ pending_start_update_callback_ = callback;
+ pending_callback_param_ = callback_param;
+ if (is_selection_pending())
+ return;
+
+ DoPendingStartUpdate();
+}
+
+void AppCacheHost::DoPendingStartUpdate() {
+ DCHECK(pending_start_update_callback_);
+
+ // TODO(michaeln): start an update if appropiate to do so
+ pending_start_update_callback_->Run(
+ false, pending_callback_param_);
+
+ pending_start_update_callback_ = NULL;
+ pending_callback_param_ = NULL;
+}
+
+void AppCacheHost::SwapCacheWithCallback(SwapCacheCallback* callback,
+ void* callback_param) {
+ DCHECK(!pending_start_update_callback_ &&
+ !pending_swap_cache_callback_ &&
+ !pending_get_status_callback_);
+
+ pending_swap_cache_callback_ = callback;
+ pending_callback_param_ = callback_param;
+ if (is_selection_pending())
+ return;
+
+ DoPendingSwapCache();
+}
+
+void AppCacheHost::DoPendingSwapCache() {
+ DCHECK(pending_swap_cache_callback_);
+
+ // TODO(michaeln): swap if we have a cache that can be swapped.
+ pending_swap_cache_callback_->Run(
+ false, pending_callback_param_);
+
+ pending_swap_cache_callback_ = NULL;
+ pending_callback_param_ = NULL;
+}
+
+AppCacheRequestHandler* AppCacheHost::CreateRequestHandler(
+ URLRequest* request,
+ bool is_main_request) {
+ if (is_main_request)
+ return new AppCacheRequestHandler(this);
+
+ if (associated_cache() && associated_cache()->is_complete())
+ return new AppCacheRequestHandler(associated_cache());
+
+ return NULL;
+}
+
+Status AppCacheHost::GetStatus() {
+ // TODO(michaeln): determine a real status value
+ Status status = associated_cache() ? IDLE : UNCACHED;
+ return status;
+}
+
+void AppCacheHost::LoadOrCreateGroup(const GURL& manifest_url) {
+ DCHECK(manifest_url.is_valid());
+ pending_selected_manifest_url_ = manifest_url;
+ service_->LoadOrCreateGroup(manifest_url, this);
+}
+
+void AppCacheHost::GroupLoadedCallback(
+ AppCacheGroup* group, const GURL& manifest_url) {
+ DCHECK(manifest_url == pending_selected_manifest_url_);
+ pending_selected_manifest_url_ = GURL::EmptyGURL();
+ FinishCacheSelection(NULL, group);
+}
+
+void AppCacheHost::LoadCache(int64 cache_id) {
+ DCHECK(cache_id != kNoCacheId);
+ pending_selected_cache_id_ = cache_id;
+ service_->LoadCache(cache_id, this);
+}
+
+void AppCacheHost::CacheLoadedCallback(AppCache* cache, int64 cache_id) {
+ DCHECK(cache_id == pending_selected_cache_id_);
+ pending_selected_cache_id_ = kNoCacheId;
+ if (cache)
+ FinishCacheSelection(cache, NULL);
+ else
+ FinishCacheSelection(NULL, 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());
+ AssociateCache(cache);
+ cache->owning_group()->StartUpdateWithHost(this);
+
+ } else if (group) {
+ // 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(new_master_entry_url_.is_valid());
+ AssociateCache(NULL); // The UpdateJob may produce one for us later.
+ group->StartUpdateWithNewMasterEntry(this, new_master_entry_url_);
+
+ } else {
+ // Otherwise, the Document is not associated with any application cache.
+ AssociateCache(NULL);
+ }
+
+ // Respond to pending callbacks now that we have a selection.
+ if (pending_get_status_callback_)
+ DoPendingGetStatus();
+ else if (pending_start_update_callback_)
+ DoPendingStartUpdate();
+ else if (pending_swap_cache_callback_)
+ DoPendingSwapCache();
+}
+
+void AppCacheHost::AssociateCache(AppCache* cache) {
+ if (associated_cache_.get()) {
+ associated_cache_->UnassociateHost(this);
+ group_ = NULL;
+ }
+
+ associated_cache_ = cache;
if (cache) {
cache->AssociateHost(this);
group_ = cache->owning_group();
+ frontend_->OnCacheSelected(host_id_, cache->cache_id(), GetStatus());
} else {
- group_ = NULL;
+ frontend_->OnCacheSelected(host_id_, kNoCacheId, UNCACHED);
}
}
diff --git a/webkit/appcache/appcache_host.h b/webkit/appcache/appcache_host.h
index 420e3d4..d8ee843 100644
--- a/webkit/appcache/appcache_host.h
+++ b/webkit/appcache/appcache_host.h
@@ -6,42 +6,126 @@
#define WEBKIT_APPCACHE_APPCACHE_HOST_H_
#include "base/ref_counted.h"
+#include "base/task.h"
+#include "base/weak_ptr.h"
+#include "googleurl/src/gurl.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
+#include "webkit/appcache/appcache.h"
+#include "webkit/appcache/appcache_interfaces.h"
+#include "webkit/appcache/appcache_service.h"
+
+class URLRequest;
namespace appcache {
class AppCache;
class AppCacheFrontend;
class AppCacheGroup;
+class AppCacheRequestHandler;
+
+typedef Callback2<Status, void*>::Type GetStatusCallback;
+typedef Callback2<bool, void*>::Type StartUpdateCallback;
+typedef Callback2<bool, void*>::Type SwapCacheCallback;
// Server-side representation of an application cache host.
-class AppCacheHost {
+class AppCacheHost : public base::SupportsWeakPtr<AppCacheHost>,
+ public AppCacheService::LoadClient {
public:
- AppCacheHost(int host_id, AppCacheFrontend* frontend);
+ AppCacheHost(int host_id, AppCacheFrontend* frontend,
+ AppCacheService* service);
~AppCacheHost();
- int host_id() { return host_id_; }
- AppCacheFrontend* frontend() { return frontend_; }
+ // 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 MarkAsForeignEntry(const GURL& document_url,
+ int64 cache_document_was_loaded_from);
+ void GetStatusWithCallback(GetStatusCallback* callback,
+ void* callback_param);
+ void StartUpdateWithCallback(StartUpdateCallback* callback,
+ void* callback_param);
+ void SwapCacheWithCallback(SwapCacheCallback* callback,
+ void* callback_param);
- AppCache* selected_cache() { return selected_cache_; }
- void set_selected_cache(AppCache* cache);
+ // Support for loading resources out of the appcache.
+ // Returns NULL if the host is not associated with a complete cache.
+ AppCacheRequestHandler* CreateRequestHandler(URLRequest* request,
+ bool is_main_request);
- bool is_selection_pending() {
- return false; // TODO(michaeln)
- }
+ // Establishes an association between this host and a cache. 'cache' may be
+ // NULL to break any existing association. Associations are established
+ // either thru the cache selection algorithm implemented (in this class),
+ // or by the update algorithm (see AppCacheUpdateJob).
+ void AssociateCache(AppCache* cache);
+
+ int host_id() const { return host_id_; }
+ AppCacheService* service() const { return service_; }
+ AppCacheFrontend* frontend() const { return frontend_; }
+ AppCache* associated_cache() const { return associated_cache_.get(); }
private:
- // identifies the corresponding appcache host in the child process
+ bool is_selection_pending() const {
+ return pending_selected_cache_id_ != kNoCacheId ||
+ !pending_selected_manifest_url_.is_empty();
+ }
+ Status GetStatus();
+ void LoadCache(int64 cache_id);
+ void LoadOrCreateGroup(const GURL& manifest_url);
+
+ // LoadClient impl
+ virtual void CacheLoadedCallback(AppCache* cache, int64 cache_id);
+ virtual void GroupLoadedCallback(AppCacheGroup* group,
+ const GURL& manifest_url);
+
+ void FinishCacheSelection(AppCache* cache, AppCacheGroup* group);
+ void DoPendingGetStatus();
+ void DoPendingStartUpdate();
+ void DoPendingSwapCache();
+
+ // Identifies the corresponding appcache host in the child process.
int host_id_;
- // application cache associated with this host, if any
- scoped_refptr<AppCache> selected_cache_;
+ // The cache associated with this host, if any.
+ scoped_refptr<AppCache> associated_cache_;
- // The reference to the appcache group ensures the group exists as long
- // as there is a host using a cache belonging to that group.
+ // The reference to the group ensures the group exists
+ // while we have an association with a cache in the group.
scoped_refptr<AppCacheGroup> group_;
- // frontend to deliver notifications about this host to child process
+ // 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.
+ AppCacheService* service_;
+
+ // 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_;
+
+ FRIEND_TEST(AppCacheTest, CleanupUnusedCache);
+ FRIEND_TEST(AppCacheGroupTest, CleanupUnusedGroup);
+ FRIEND_TEST(AppCacheHostTest, Basic);
+ FRIEND_TEST(AppCacheHostTest, SelectNoCache);
+ FRIEND_TEST(AppCacheHostTest, ForeignEntry);
+ FRIEND_TEST(AppCacheHostTest, FailedCacheLoad);
+ FRIEND_TEST(AppCacheHostTest, FailedGroupLoad);
+ DISALLOW_COPY_AND_ASSIGN(AppCacheHost);
};
} // namespace appcache
diff --git a/webkit/appcache/appcache_host_unittest.cc b/webkit/appcache/appcache_host_unittest.cc
new file mode 100644
index 0000000..cd1497b
--- /dev/null
+++ b/webkit/appcache/appcache_host_unittest.cc
@@ -0,0 +1,230 @@
+// Copyright (c) 2009 The Chromium Authos. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/appcache/appcache.h"
+#include "webkit/appcache/appcache_group.h"
+#include "webkit/appcache/appcache_host.h"
+#include "webkit/appcache/appcache_service.h"
+
+namespace appcache {
+
+class AppCacheHostTest : public testing::Test {
+ public:
+ AppCacheHostTest() {
+ get_status_callback_.reset(
+ NewCallback(this, &AppCacheHostTest::GetStatusCallback));
+ start_update_callback_.reset(
+ NewCallback(this, &AppCacheHostTest::StartUpdateCallback));
+ swap_cache_callback_.reset(
+ NewCallback(this, &AppCacheHostTest::SwapCacheCallback));
+ }
+
+ class MockFrontend : public AppCacheFrontend {
+ public:
+ MockFrontend()
+ : last_host_id_(-222), last_cache_id_(-222),
+ last_status_(appcache::OBSOLETE) {
+ }
+
+ virtual void OnCacheSelected(int host_id, int64 cache_id ,
+ appcache::Status status) {
+ last_host_id_ = host_id;
+ last_cache_id_ = cache_id;
+ last_status_ = status;
+ }
+
+ virtual void OnStatusChanged(const std::vector<int>& host_ids,
+ appcache::Status status) {
+ }
+
+ virtual void OnEventRaised(const std::vector<int>& host_ids,
+ appcache::EventID event_id) {
+ }
+
+ int last_host_id_;
+ int64 last_cache_id_;
+ appcache::Status last_status_;
+ };
+
+ void GetStatusCallback(Status status, void* param) {
+ last_status_result_ = status;
+ last_callback_param_ = param;
+ }
+
+ void StartUpdateCallback(bool result, void* param) {
+ last_start_result_ = result;
+ last_callback_param_ = param;
+ }
+
+ void SwapCacheCallback(bool result, void* param) {
+ last_swap_result_ = result;
+ last_callback_param_ = param;
+ }
+
+ // Mock classes for the 'host' to work with
+ AppCacheService service_; // TODO(michaeln): make service mockable?
+ MockFrontend mock_frontend_;
+
+ // Mock callbacks we expect to receive from the 'host'
+ scoped_ptr<appcache::GetStatusCallback> get_status_callback_;
+ scoped_ptr<appcache::StartUpdateCallback> start_update_callback_;
+ scoped_ptr<appcache::SwapCacheCallback> swap_cache_callback_;
+
+ Status last_status_result_;
+ bool last_swap_result_;
+ bool last_start_result_;
+ void* last_callback_param_;
+};
+
+TEST_F(AppCacheHostTest, Basic) {
+ // Construct a host and test what state it appears to be in.
+ AppCacheHost host(1, &mock_frontend_, &service_);
+ EXPECT_EQ(1, host.host_id());
+ EXPECT_EQ(&service_, host.service());
+ EXPECT_EQ(&mock_frontend_, host.frontend());
+ EXPECT_EQ(NULL, host.associated_cache());
+ EXPECT_FALSE(host.is_selection_pending());
+
+ // See that the callbacks are delivered immediately
+ // and respond as if there is no cache selected.
+ last_status_result_ = OBSOLETE;
+ host.GetStatusWithCallback(get_status_callback_.get(),
+ reinterpret_cast<void*>(1));
+ EXPECT_EQ(UNCACHED, last_status_result_);
+ EXPECT_EQ(reinterpret_cast<void*>(1), last_callback_param_);
+
+ last_start_result_ = true;
+ host.StartUpdateWithCallback(start_update_callback_.get(),
+ reinterpret_cast<void*>(2));
+ EXPECT_FALSE(last_start_result_);
+ EXPECT_EQ(reinterpret_cast<void*>(2), last_callback_param_);
+
+ last_swap_result_ = true;
+ host.SwapCacheWithCallback(swap_cache_callback_.get(),
+ reinterpret_cast<void*>(3));
+ EXPECT_FALSE(last_swap_result_);
+ EXPECT_EQ(reinterpret_cast<void*>(3), last_callback_param_);
+}
+
+TEST_F(AppCacheHostTest, SelectNoCache) {
+ // Reset our mock frontend
+ mock_frontend_.last_cache_id_ = -333;
+ mock_frontend_.last_host_id_ = -333;
+ mock_frontend_.last_status_ = OBSOLETE;
+
+ AppCacheHost host(1, &mock_frontend_, &service_);
+ host.SelectCache(GURL("http://whatever/"), kNoCacheId, GURL::EmptyGURL());
+
+ // We should have received an OnCacheSelected msg
+ EXPECT_EQ(1, mock_frontend_.last_host_id_);
+ EXPECT_EQ(kNoCacheId, mock_frontend_.last_cache_id_);
+ EXPECT_EQ(UNCACHED, mock_frontend_.last_status_);
+
+ // Otherwise, see that it respond as if there is no cache selected.
+ EXPECT_EQ(1, host.host_id());
+ EXPECT_EQ(&service_, host.service());
+ EXPECT_EQ(&mock_frontend_, host.frontend());
+ EXPECT_EQ(NULL, host.associated_cache());
+ EXPECT_FALSE(host.is_selection_pending());
+}
+
+TEST_F(AppCacheHostTest, ForeignEntry) {
+ // Reset our mock frontend
+ mock_frontend_.last_cache_id_ = -333;
+ mock_frontend_.last_host_id_ = -333;
+ mock_frontend_.last_status_ = OBSOLETE;
+
+ AppCacheHost host(1, &mock_frontend_, &service_);
+ host.MarkAsForeignEntry(GURL("http://whatever/"), 22);
+
+ // We should have received an OnCacheSelected msg for kNoCacheId.
+ EXPECT_EQ(1, mock_frontend_.last_host_id_);
+ EXPECT_EQ(kNoCacheId, mock_frontend_.last_cache_id_);
+ EXPECT_EQ(UNCACHED, mock_frontend_.last_status_);
+
+ // See that it respond as if there is no cache selected.
+ EXPECT_EQ(1, host.host_id());
+ EXPECT_EQ(&service_, host.service());
+ EXPECT_EQ(&mock_frontend_, host.frontend());
+ EXPECT_EQ(NULL, host.associated_cache());
+ EXPECT_FALSE(host.is_selection_pending());
+}
+
+
+TEST_F(AppCacheHostTest, FailedCacheLoad) {
+ // Reset our mock frontend
+ mock_frontend_.last_cache_id_ = -333;
+ mock_frontend_.last_host_id_ = -333;
+ mock_frontend_.last_status_ = OBSOLETE;
+
+ AppCacheHost host(1, &mock_frontend_, &service_);
+ EXPECT_FALSE(host.is_selection_pending());
+
+ const int kMockCacheId = 333;
+
+ // Put it in a state where we're waiting on a cache
+ // load prior to finishing cache selection.
+ host.pending_selected_cache_id_ = kMockCacheId;
+ EXPECT_TRUE(host.is_selection_pending());
+
+ // The callback should not occur until we finish cache selection.
+ last_status_result_ = OBSOLETE;
+ last_callback_param_ = reinterpret_cast<void*>(-1);
+ host.GetStatusWithCallback(get_status_callback_.get(),
+ reinterpret_cast<void*>(1));
+ EXPECT_EQ(OBSOLETE, last_status_result_);
+ EXPECT_EQ(reinterpret_cast<void*>(-1), last_callback_param_);
+
+ // Satisfy the load with NULL, a failure.
+ host.CacheLoadedCallback(NULL, kMockCacheId);
+
+ // Cache selection should have finished
+ EXPECT_FALSE(host.is_selection_pending());
+ EXPECT_EQ(1, mock_frontend_.last_host_id_);
+ EXPECT_EQ(kNoCacheId, mock_frontend_.last_cache_id_);
+ EXPECT_EQ(UNCACHED, mock_frontend_.last_status_);
+
+ // Callback should have fired upon completing the cache load too.
+ EXPECT_EQ(UNCACHED, last_status_result_);
+ EXPECT_EQ(reinterpret_cast<void*>(1), last_callback_param_);
+}
+
+TEST_F(AppCacheHostTest, FailedGroupLoad) {
+ AppCacheHost host(1, &mock_frontend_, &service_);
+
+ const GURL kMockManifestUrl("http://foo.bar/baz");
+
+ // Put it in a state where we're waiting on a cache
+ // load prior to finishing cache selection.
+ host.pending_selected_manifest_url_ = kMockManifestUrl;
+ EXPECT_TRUE(host.is_selection_pending());
+
+ // The callback should not occur until we finish cache selection.
+ last_status_result_ = OBSOLETE;
+ last_callback_param_ = reinterpret_cast<void*>(-1);
+ host.GetStatusWithCallback(get_status_callback_.get(),
+ reinterpret_cast<void*>(1));
+ EXPECT_EQ(OBSOLETE, last_status_result_);
+ EXPECT_EQ(reinterpret_cast<void*>(-1), last_callback_param_);
+
+ // Satisfy the load will NULL, a failure.
+ host.GroupLoadedCallback(NULL, kMockManifestUrl);
+
+ // Cache selection should have finished
+ EXPECT_FALSE(host.is_selection_pending());
+ EXPECT_EQ(1, mock_frontend_.last_host_id_);
+ EXPECT_EQ(kNoCacheId, mock_frontend_.last_cache_id_);
+ EXPECT_EQ(UNCACHED, mock_frontend_.last_status_);
+
+ // Callback should have fired upon completing the group load.
+ EXPECT_EQ(UNCACHED, last_status_result_);
+ EXPECT_EQ(reinterpret_cast<void*>(1), last_callback_param_);
+}
+
+// TODO(michaeln): Flesh these tests out more.
+
+} // namespace appcache
+
diff --git a/webkit/appcache/appcache_interceptor.cc b/webkit/appcache/appcache_interceptor.cc
index a66fea8..88bcd56 100644
--- a/webkit/appcache/appcache_interceptor.cc
+++ b/webkit/appcache/appcache_interceptor.cc
@@ -7,84 +7,51 @@
#include "webkit/appcache/appcache_backend_impl.h"
#include "webkit/appcache/appcache_host.h"
#include "webkit/appcache/appcache_interfaces.h"
+#include "webkit/appcache/appcache_request_handler.h"
#include "webkit/appcache/appcache_service.h"
namespace appcache {
-// Extra info we associate with requests for use at MaybeIntercept time. This
-// info is deleted when the URLRequest is deleted which occurs after the
-// request is complete and all data has been read.
-struct AppCacheInterceptor::ExtraInfo : public URLRequest::UserData {
- // Inputs, extra request info
- AppCacheService* service;
- int process_id;
- int host_id;
- ResourceType::Type resource_type;
-
- // Outputs, extra response info
- int64 cache_id;
- GURL manifest_url;
-
- // The host associated with the request
- // TODO(michaeln): Be careful with this data member, its not clear
- // if a URLRequest can outlive the associated host. As we get further
- // along, we'll need to notify reqeust waiting on cache selection to
- // allow them to continue upon completion of selection. But we also need
- // to handle navigating away from the page away prior to selection being
- // complete.
- AppCacheHost* host_;
-
- ExtraInfo(AppCacheService* service, int process_id, int host_id,
- ResourceType::Type resource_type, AppCacheHost* host)
- : service(service), process_id(process_id), host_id(host_id),
- resource_type(resource_type), cache_id(kNoCacheId), host_(host) {
- }
-
- static void SetInfo(URLRequest* request, ExtraInfo* info) {
- request->SetUserData(instance(), info); // request takes ownership
- }
-
- static ExtraInfo* GetInfo(URLRequest* request) {
- return static_cast<ExtraInfo*>(request->GetUserData(instance()));
- }
-};
-
-static bool IsMainRequest(ResourceType::Type type) {
- // TODO(michaeln): SHARED_WORKER type?
- return ResourceType::IsFrame(type);
+void AppCacheInterceptor::SetHandler(
+ URLRequest* request, AppCacheRequestHandler* handler) {
+ request->SetUserData(instance(), handler); // request takes ownership
+}
+
+AppCacheRequestHandler* AppCacheInterceptor::GetHandler(URLRequest* request) {
+ return reinterpret_cast<AppCacheRequestHandler*>(
+ request->GetUserData(instance()));
}
void AppCacheInterceptor::SetExtraRequestInfo(
URLRequest* request, AppCacheService* service, int process_id,
int host_id, ResourceType::Type resource_type) {
- if (service && (host_id != kNoHostId)) {
- AppCacheHost* host = service->GetBackend(process_id)->GetHost(host_id);
- DCHECK(host);
- if (IsMainRequest(resource_type) || host->selected_cache() ||
- host->is_selection_pending()) {
- ExtraInfo* info = new ExtraInfo(service, process_id,
- host_id, resource_type, host);
- ExtraInfo::SetInfo(request, info);
- }
- }
+ if (!service || (host_id == kNoHostId))
+ return;
+
+ // TODO(michaeln): An invalid host id is indicative of bad data
+ // from a child process. How should we handle that here?
+ AppCacheHost* host = service->GetBackend(process_id)->GetHost(host_id);
+ if (!host)
+ return;
+
+ // TODO(michaeln): SHARED_WORKER type too
+ bool is_main_request = ResourceType::IsFrame(resource_type);
+
+ // Create a handler for this request and associate it with the request.
+ AppCacheRequestHandler* handler =
+ host->CreateRequestHandler(request, is_main_request);
+ if (handler)
+ SetHandler(request, handler);
}
void AppCacheInterceptor::GetExtraResponseInfo(URLRequest* request,
int64* cache_id,
GURL* manifest_url) {
- ExtraInfo* info = ExtraInfo::GetInfo(request);
- if (info) {
- // TODO(michaeln): If this is a main request and it was retrieved from
- // an appcache, ensure that appcache survives the frame navigation. The
- // AppCacheHost should hold reference to that cache to prevent it from
- // being dropped from the in-memory collection of AppCaches. When cache
- // selection occurs, that extra reference should be dropped.
- *cache_id = info->cache_id;
- *manifest_url = info->manifest_url;
- } else {
- DCHECK(*cache_id == kNoCacheId);
- DCHECK(manifest_url->is_empty());
- }
+ DCHECK(*cache_id == kNoCacheId);
+ DCHECK(manifest_url->is_empty());
+ AppCacheRequestHandler* handler = GetHandler(request);
+ if (handler)
+ handler->GetExtraResponseInfo(cache_id, manifest_url);
}
AppCacheInterceptor::AppCacheInterceptor() {
@@ -96,30 +63,27 @@ AppCacheInterceptor::~AppCacheInterceptor() {
}
URLRequestJob* AppCacheInterceptor::MaybeIntercept(URLRequest* request) {
- ExtraInfo* info = ExtraInfo::GetInfo(request);
- if (!info)
+ AppCacheRequestHandler* handler = GetHandler(request);
+ if (!handler)
return NULL;
- // TODO(michaeln): write me
- return NULL;
+ return handler->MaybeLoadResource(request);
}
URLRequestJob* AppCacheInterceptor::MaybeInterceptRedirect(
URLRequest* request,
const GURL& location) {
- ExtraInfo* info = ExtraInfo::GetInfo(request);
- if (!info)
+ AppCacheRequestHandler* handler = GetHandler(request);
+ if (!handler)
return NULL;
- // TODO(michaeln): write me
- return NULL;
+ return handler->MaybeLoadFallbackForRedirect(request, location);
}
URLRequestJob* AppCacheInterceptor::MaybeInterceptResponse(
URLRequest* request) {
- ExtraInfo* info = ExtraInfo::GetInfo(request);
- if (!info)
+ AppCacheRequestHandler* handler = GetHandler(request);
+ if (!handler)
return NULL;
- // TODO(michaeln): write me
- return NULL;
+ return handler->MaybeLoadFallbackForResponse(request);
}
} // namespace appcache
diff --git a/webkit/appcache/appcache_interceptor.h b/webkit/appcache/appcache_interceptor.h
index 51b9a89..7af9aed 100644
--- a/webkit/appcache/appcache_interceptor.h
+++ b/webkit/appcache/appcache_interceptor.h
@@ -12,6 +12,7 @@
namespace appcache {
+class AppCacheRequestHandler;
class AppCacheService;
// An interceptor to hijack requests and potentially service them out of
@@ -46,12 +47,17 @@ class AppCacheInterceptor : public URLRequest::Interceptor {
private:
friend struct DefaultSingletonTraits<AppCacheInterceptor>;
+
static AppCacheInterceptor* instance() {
return Singleton<AppCacheInterceptor>::get();
}
- struct ExtraInfo;
+
AppCacheInterceptor();
virtual ~AppCacheInterceptor();
+
+ static void SetHandler(URLRequest* request, AppCacheRequestHandler* handler);
+ static AppCacheRequestHandler* GetHandler(URLRequest* request);
+
DISALLOW_COPY_AND_ASSIGN(AppCacheInterceptor);
};
diff --git a/webkit/appcache/appcache_request_handler.cc b/webkit/appcache/appcache_request_handler.cc
new file mode 100644
index 0000000..322e8bc
--- /dev/null
+++ b/webkit/appcache/appcache_request_handler.cc
@@ -0,0 +1,63 @@
+// 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 "webkit/appcache/appcache_request_handler.h"
+
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_job.h"
+#include "webkit/appcache/appcache.h"
+
+namespace appcache {
+
+// AppCacheRequestHandler -----------------------------------------------------
+
+static bool IsHttpOrHttpsGetOrEquivalent(URLRequest* request) {
+ return false; // TODO(michaeln): write me
+}
+
+AppCacheRequestHandler::AppCacheRequestHandler(AppCacheHost* host)
+ : is_main_request_(true), cache_id_(kNoCacheId),
+ host_(host->AsWeakPtr()), service_(host->service()) {
+}
+
+AppCacheRequestHandler::AppCacheRequestHandler(AppCache* cache)
+ : is_main_request_(false), cache_id_(kNoCacheId),
+ cache_(cache), service_(cache->service()) {
+}
+
+void AppCacheRequestHandler::GetExtraResponseInfo(
+ int64* cache_id, GURL* manifest_url) {
+ // TODO(michaeln): If this is a main request and it was retrieved from
+ // an appcache, ensure that appcache survives the frame navigation. The
+ // AppCacheHost should hold reference to that cache to prevent it from
+ // being dropped from the in-memory collection of AppCaches. When cache
+ // selection occurs, that extra reference should be dropped. Perhaps
+ // maybe: if (is_main) host->LoadCacheOfMainResource(cache_id);
+}
+
+URLRequestJob* AppCacheRequestHandler::MaybeLoadResource(URLRequest* request) {
+ if (!IsHttpOrHttpsGetOrEquivalent(request))
+ return NULL;
+ // TODO(michaeln): write me
+ return NULL;
+}
+
+URLRequestJob* AppCacheRequestHandler::MaybeLoadFallbackForRedirect(
+ URLRequest* request, const GURL& location) {
+ if (!IsHttpOrHttpsGetOrEquivalent(request))
+ return NULL;
+ // TODO(michaeln): write me
+ return NULL;
+}
+
+URLRequestJob* AppCacheRequestHandler::MaybeLoadFallbackForResponse(
+ URLRequest* request) {
+ if (!IsHttpOrHttpsGetOrEquivalent(request))
+ return NULL;
+ // TODO(michaeln): write me
+ return NULL;
+}
+
+} // namespace appcache
+
diff --git a/webkit/appcache/appcache_request_handler.h b/webkit/appcache/appcache_request_handler.h
new file mode 100644
index 0000000..78fe9c3
--- /dev/null
+++ b/webkit/appcache/appcache_request_handler.h
@@ -0,0 +1,53 @@
+// 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 WEBKIT_APPCACHE_APPCACHE_REQUEST_HANDLER_H_
+#define WEBKIT_APPCACHE_APPCACHE_REQUEST_HANDLER_H_
+
+#include "net/url_request/url_request.h"
+#include "webkit/appcache/appcache_host.h"
+
+class URLRequest;
+class URLRequestJob;
+
+namespace appcache {
+
+// An instance is created for each URLRequest. The instance survives all
+// http transactions involved in the processing of its 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 AppCacheRequestHandler : public URLRequest::UserData {
+ public:
+ // Should be called on each request intercept opportunity.
+ URLRequestJob* MaybeLoadResource(URLRequest* request);
+ URLRequestJob* MaybeLoadFallbackForRedirect(URLRequest* request,
+ const GURL& location);
+ URLRequestJob* MaybeLoadFallbackForResponse(URLRequest* request);
+
+ void GetExtraResponseInfo(int64* cache_id, GURL* manifest_url);
+
+ private:
+ friend class AppCacheHost;
+
+ // Ctor for main resource loads.
+ explicit AppCacheRequestHandler(AppCacheHost* host);
+
+ // Ctor for subresource loads when the cache is loaded.
+ explicit AppCacheRequestHandler(AppCache* cache);
+
+ // Main vs subresource loads are very different.
+ // TODO(michaeln): maybe have two derived classes?
+ bool is_main_request_;
+ int64 cache_id_;
+ scoped_refptr<AppCache> cache_;
+ base::WeakPtr<AppCacheHost> host_;
+ scoped_refptr<URLRequestJob> job_;
+ AppCacheService* service_;
+};
+
+} // namespace appcache
+
+#endif // WEBKIT_APPCACHE_APPCACHE_REQUEST_HANDLER_H_
+
diff --git a/webkit/appcache/appcache_service.cc b/webkit/appcache/appcache_service.cc
index 029c4ef..6de7332 100644
--- a/webkit/appcache/appcache_service.cc
+++ b/webkit/appcache/appcache_service.cc
@@ -5,8 +5,10 @@
#include "webkit/appcache/appcache_service.h"
#include "base/logging.h"
+#include "base/ref_counted.h"
#include "webkit/appcache/appcache.h"
#include "webkit/appcache/appcache_backend_impl.h"
+#include "webkit/appcache/appcache_entry.h"
#include "webkit/appcache/appcache_group.h"
namespace appcache {
@@ -60,4 +62,39 @@ void AppCacheService::RemoveGroup(AppCacheGroup* group) {
groups_.erase(group->manifest_url());
}
+void AppCacheService::LoadCache(int64 id, LoadClient* client) {
+ // TODO(michaeln): actually retrieve from storage if needed
+ client->CacheLoadedCallback(GetCache(id), id);
+}
+
+void AppCacheService::LoadOrCreateGroup(const GURL& manifest_url,
+ LoadClient* client) {
+ // TODO(michaeln): actually retrieve from storage
+ scoped_refptr<AppCacheGroup> group = GetGroup(manifest_url);
+ if (!group.get()) {
+ group = new AppCacheGroup(this, manifest_url);
+ DCHECK(GetGroup(manifest_url));
+ }
+ client->GroupLoadedCallback(group.get(), manifest_url);
+}
+
+void AppCacheService::CancelLoads(LoadClient* client) {
+ // TODO(michaeln): remove client from loading lists
+}
+
+void AppCacheService::MarkAsForeignEntry(const GURL& entry_url,
+ int64 cache_id) {
+ // Update the in-memory cache.
+ AppCache* cache = GetCache(cache_id);
+ if (cache) {
+ AppCacheEntry* entry = cache->GetEntry(entry_url);
+ DCHECK(entry);
+ if (entry)
+ entry->add_types(AppCacheEntry::FOREIGN);
+ }
+
+ // TODO(michaeln): actually update in storage, and if this cache is
+ // being loaded be sure to update the memory cache upon load completion.
+}
+
} // namespace appcache
diff --git a/webkit/appcache/appcache_service.h b/webkit/appcache/appcache_service.h
index 0ccad80..0d7609d 100644
--- a/webkit/appcache/appcache_service.h
+++ b/webkit/appcache/appcache_service.h
@@ -12,6 +12,7 @@
#include "base/hash_tables.h"
#include "base/file_path.h"
#include "base/ref_counted.h"
+#include "base/task.h"
#include "net/url_request/url_request_context.h"
#include "googleurl/src/gurl.h"
@@ -26,6 +27,17 @@ class AppCacheGroup;
// exclusive access to it's cache_directory on disk.
class AppCacheService {
public:
+
+ class LoadClient {
+ public:
+ virtual ~LoadClient() {}
+
+ // If a load fails the object pointer will be NULL.
+ virtual void CacheLoadedCallback(AppCache* cache, int64 cache_id) = 0;
+ virtual void GroupLoadedCallback(AppCacheGroup* cache,
+ const GURL& manifest_url) = 0;
+ };
+
AppCacheService();
virtual ~AppCacheService();
@@ -42,30 +54,41 @@ class AppCacheService {
// TODO(jennb): API to set service settings, like file paths for storage
- // track which processes are using this appcache service
+ // Track which processes are using this appcache service.
void RegisterBackend(AppCacheBackendImpl* backend_impl);
void UnregisterBackend(AppCacheBackendImpl* backend_impl);
-
- void AddCache(AppCache* cache);
- void RemoveCache(AppCache* cache);
- void AddGroup(AppCacheGroup* group);
- void RemoveGroup(AppCacheGroup* group);
-
AppCacheBackendImpl* GetBackend(int id) {
BackendMap::iterator it = backends_.find(id);
return (it != backends_.end()) ? it->second : NULL;
}
+ // Track what we have in or in-memory cache.
+ void AddCache(AppCache* cache);
+ void RemoveCache(AppCache* cache);
+ void AddGroup(AppCacheGroup* group);
+ void RemoveGroup(AppCacheGroup* group);
AppCache* GetCache(int64 id) {
CacheMap::iterator it = caches_.find(id);
return (it != caches_.end()) ? it->second : NULL;
}
-
AppCacheGroup* GetGroup(const GURL& manifest_url) {
GroupMap::iterator it = groups_.find(manifest_url);
return (it != groups_.end()) ? it->second : NULL;
}
+ // Load caches and groups from storage. If the request object
+ // is already in memory, the client is called immediately
+ // without returning to the message loop.
+ void LoadCache(int64 id, LoadClient* client);
+ void LoadOrCreateGroup(const GURL& manifest_url,
+ LoadClient* client);
+
+ // Cancels pending callbacks for this client.
+ void CancelLoads(LoadClient* client);
+
+ // Updates in memory and persistent storage.
+ void MarkAsForeignEntry(const GURL& entry_url, int64 cache_id);
+
// The service generates unique storage ids for different object types.
int64 NewCacheId() { return ++last_cache_id_; }
int64 NewGroupId() { return ++last_group_id_; }
@@ -90,15 +113,14 @@ class AppCacheService {
typedef std::map<int, AppCacheBackendImpl*> BackendMap;
BackendMap backends_;
+ // Where we save our data.
FilePath cache_directory_;
// Context for use during cache updates.
scoped_refptr<URLRequestContext> request_context_;
- // TODO(jennb): info about appcache storage
- // AppCacheDatabase db_;
- // DiskCache response_storage_;
-
+ // TODO(michaeln): cache and group loading book keeping.
+ // TODO(michaeln): database and response storage
// TODO(jennb): service state: e.g. reached quota?
};
diff --git a/webkit/appcache/appcache_service_unittest.cc b/webkit/appcache/appcache_service_unittest.cc
index c58374f..d142e8b 100644
--- a/webkit/appcache/appcache_service_unittest.cc
+++ b/webkit/appcache/appcache_service_unittest.cc
@@ -7,17 +7,11 @@
#include "webkit/appcache/appcache_group.h"
#include "webkit/appcache/appcache_service.h"
-using appcache::AppCache;
-using appcache::AppCacheGroup;
-using appcache::AppCacheService;
-
-namespace {
+namespace appcache {
class AppCacheServiceTest : public testing::Test {
};
-} // namespace
-
TEST(AppCacheServiceTest, AddRemoveCache) {
AppCacheService service;
scoped_refptr<AppCache> cache = new AppCache(&service, 111);
@@ -31,10 +25,12 @@ TEST(AppCacheServiceTest, AddRemoveCache) {
TEST(AppCacheServiceTest, AddRemoveGroup) {
AppCacheService service;
scoped_refptr<AppCacheGroup> group =
- new AppCacheGroup(&service, GURL::EmptyGURL());
+ new AppCacheGroup(&service, GURL::EmptyGURL());
service.RemoveGroup(group);
// Removing non-existing group from service should not fail.
AppCacheService dummy;
dummy.RemoveGroup(group);
}
+
+} // namespace appcache
diff --git a/webkit/appcache/appcache_unittest.cc b/webkit/appcache/appcache_unittest.cc
index 951cb8b..b5c1772 100644
--- a/webkit/appcache/appcache_unittest.cc
+++ b/webkit/appcache/appcache_unittest.cc
@@ -4,33 +4,29 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "webkit/appcache/appcache.h"
+#include "webkit/appcache/appcache_frontend_impl.h"
#include "webkit/appcache/appcache_host.h"
#include "webkit/appcache/appcache_service.h"
-using appcache::AppCache;
-using appcache::AppCacheEntry;
-using appcache::AppCacheHost;
-using appcache::AppCacheService;
-
-namespace {
+namespace appcache {
class AppCacheTest : public testing::Test {
};
-} // namespace
-
TEST(AppCacheTest, CleanupUnusedCache) {
AppCacheService service;
+ AppCacheFrontendImpl frontend;
AppCache* cache = new AppCache(&service, 111);
+ cache->set_complete(true);
- AppCacheHost host1(1, NULL);
- AppCacheHost host2(2, NULL);
+ AppCacheHost host1(1, &frontend, &service);
+ AppCacheHost host2(2, &frontend, &service);
- host1.set_selected_cache(cache);
- host2.set_selected_cache(cache);
+ host1.AssociateCache(cache);
+ host2.AssociateCache(cache);
- host1.set_selected_cache(NULL);
- host2.set_selected_cache(NULL);
+ host1.AssociateCache(NULL);
+ host2.AssociateCache(NULL);
}
TEST(AppCacheTest, AddModifyEntry) {
@@ -53,3 +49,6 @@ TEST(AppCacheTest, AddModifyEntry) {
cache->GetEntry(kUrl1)->types());
EXPECT_EQ(entry2.types(), cache->GetEntry(kUrl2)->types()); // unchanged
}
+
+} // namespace appacache
+
diff --git a/webkit/appcache/manifest_parser_unittest.cc b/webkit/appcache/manifest_parser_unittest.cc
index bcc145b..9fb42e0 100644
--- a/webkit/appcache/manifest_parser_unittest.cc
+++ b/webkit/appcache/manifest_parser_unittest.cc
@@ -8,17 +8,11 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "webkit/appcache/manifest_parser.h"
-using appcache::FallbackNamespace;
-using appcache::Manifest;
-using appcache::ParseManifest;
-
-namespace {
+namespace appcache {
class ManifestParserTest : public testing::Test {
};
-} // namespace
-
TEST(ManifestParserTest, NoData) {
GURL url;
Manifest manifest;
@@ -306,3 +300,6 @@ TEST(ManifestParserTest, UnusualUtf8) {
EXPECT_TRUE(urls.find("http://bad.com/%EF%BF%BDinvalidutf8") != urls.end());
EXPECT_TRUE(urls.find("http://bad.com/nonbmp%F1%84%AB%BC") != urls.end());
}
+
+} // namespace appcache
+
diff --git a/webkit/appcache/web_application_cache_host_impl.cc b/webkit/appcache/web_application_cache_host_impl.cc
index 379a80d..dfba39b 100644
--- a/webkit/appcache/web_application_cache_host_impl.cc
+++ b/webkit/appcache/web_application_cache_host_impl.cc
@@ -104,12 +104,15 @@ bool WebApplicationCacheHostImpl::selectCacheWithManifest(
return true;
}
+ DCHECK(should_capture_main_response_ == NO);
+
// Check for 'foreign' entries.
GURL main_response_manifest_gurl(main_response_.appCacheManifestURL());
if (main_response_manifest_gurl != manifest_gurl) {
backend_->MarkAsForeignEntry(host_id_, main_response_url_,
main_response_.appCacheID());
- selectCacheWithoutManifest();
+ has_cached_status_ = true;
+ cached_status_ = UNCACHED;
return false; // the navigation will be restarted
}