summaryrefslogtreecommitdiffstats
path: root/webkit/appcache
diff options
context:
space:
mode:
authorjennb@chromium.org <jennb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-12-01 00:54:57 +0000
committerjennb@chromium.org <jennb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-12-01 00:54:57 +0000
commita17b674580be4397cf56e2c955fac9f2c4a3bfaa (patch)
tree09e01865679b16cbaa27d55de7ee02930d304ec2 /webkit/appcache
parentb2fb9ff65d58beffec6a59853e1c3cfa5ecdb173 (diff)
downloadchromium_src-a17b674580be4397cf56e2c955fac9f2c4a3bfaa.zip
chromium_src-a17b674580be4397cf56e2c955fac9f2c4a3bfaa.tar.gz
chromium_src-a17b674580be4397cf56e2c955fac9f2c4a3bfaa.tar.bz2
Appcache update support for pending master entries:
- Update process issues a URL request to fetch pending master entries. - Pending master entry fetch logic kept separate from regular url fetching as this will be the case in the long-term solution. - No optimizations to avoid issuing URL request if pending master entry is also listed in the manifest. (simpler) - Only optimized to prevent refetching something that has already been successfully fetched. Long-term optimized solution should be to siphon the responses as the master resource is downloaded instead of having the update job issue URL requests. TEST=new tests for update jobs with pending master entries BUG=none Review URL: http://codereview.chromium.org/402098 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@33394 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/appcache')
-rw-r--r--webkit/appcache/appcache_host.h7
-rw-r--r--webkit/appcache/appcache_update_job.cc377
-rw-r--r--webkit/appcache/appcache_update_job.h39
-rw-r--r--webkit/appcache/appcache_update_job_unittest.cc760
-rw-r--r--webkit/appcache/data/appcache_unittest/bad-manifest2
-rw-r--r--webkit/appcache/data/appcache_unittest/bad-manifest.mock-http-headers2
6 files changed, 1103 insertions, 84 deletions
diff --git a/webkit/appcache/appcache_host.h b/webkit/appcache/appcache_host.h
index 0622e3f..f154bae 100644
--- a/webkit/appcache/appcache_host.h
+++ b/webkit/appcache/appcache_host.h
@@ -84,6 +84,12 @@ class AppCacheHost : public AppCacheStorage::Delegate,
// Used to ensure that a loaded appcache survives a frame navigation.
void LoadMainResourceCache(int64 cache_id);
+ // 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_; }
AppCacheService* service() const { return service_; }
AppCacheFrontend* frontend() const { return frontend_; }
@@ -163,6 +169,7 @@ class AppCacheHost : public AppCacheStorage::Delegate,
ObserverList<Observer> observers_;
friend class AppCacheRequestHandlerTest;
+ friend class AppCacheUpdateJobTest;
FRIEND_TEST(AppCacheTest, CleanupUnusedCache);
FRIEND_TEST(AppCacheGroupTest, CleanupUnusedGroup);
FRIEND_TEST(AppCacheHostTest, Basic);
diff --git a/webkit/appcache/appcache_update_job.cc b/webkit/appcache/appcache_update_job.cc
index 83190d0..8cd752f 100644
--- a/webkit/appcache/appcache_update_job.cc
+++ b/webkit/appcache/appcache_update_job.cc
@@ -10,7 +10,6 @@
#include "net/base/io_buffer.h"
#include "net/base/load_flags.h"
#include "webkit/appcache/appcache_group.h"
-#include "webkit/appcache/appcache_host.h"
namespace appcache {
@@ -25,6 +24,7 @@ class UpdateJobInfo : public URLRequest::UserData {
enum RequestType {
MANIFEST_FETCH,
URL_FETCH,
+ MASTER_ENTRY_FETCH,
MANIFEST_REFETCH,
};
@@ -123,6 +123,8 @@ AppCacheUpdateJob::~AppCacheUpdateJob() {
DCHECK(!manifest_url_request_);
DCHECK(pending_url_fetches_.empty());
DCHECK(!inprogress_cache_);
+ DCHECK(pending_master_entries_.empty());
+ DCHECK(master_entry_fetches_.empty());
if (group_)
group_->SetUpdateStatus(AppCacheGroup::IDLE);
@@ -132,23 +134,40 @@ void AppCacheUpdateJob::StartUpdate(AppCacheHost* host,
const GURL& new_master_resource) {
DCHECK(group_->update_job() == this);
+ bool is_new_pending_master_entry = false;
if (!new_master_resource.is_empty()) {
- /* TODO(jennb): uncomment when processing master entries is implemented
+ 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()) {
+ // TODO(jennb): requeue in group
+ 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.
- appcache::AppCacheGroup::UpdateStatus update_status = group_->update_status();
+ AppCacheGroup::UpdateStatus update_status = group_->update_status();
if (update_status == AppCacheGroup::CHECKING ||
update_status == AppCacheGroup::DOWNLOADING) {
if (host) {
NotifySingleHost(host, CHECKING_EVENT);
if (update_status == AppCacheGroup::DOWNLOADING)
NotifySingleHost(host, 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;
}
@@ -164,6 +183,11 @@ void AppCacheUpdateJob::StartUpdate(AppCacheHost* host,
NotifySingleHost(host, CHECKING_EVENT);
}
+ if (!new_master_resource.is_empty()) {
+ AddMasterEntryToFetchList(host, new_master_resource,
+ is_new_pending_master_entry);
+ }
+
FetchManifest(true);
}
@@ -187,7 +211,8 @@ void AppCacheUpdateJob::OnResponseStarted(URLRequest *request) {
// completion before reading any response data.
UpdateJobInfo* info =
static_cast<UpdateJobInfo*>(request->GetUserData(this));
- if (info->type_ == UpdateJobInfo::URL_FETCH) {
+ if (info->type_ == UpdateJobInfo::URL_FETCH ||
+ info->type_ == UpdateJobInfo::MASTER_ENTRY_FETCH) {
info->SetUpResponseWriter(
service_->storage()->CreateResponseWriter(manifest_url_),
this, request);
@@ -250,6 +275,7 @@ bool AppCacheUpdateJob::ConsumeResponseData(URLRequest* request,
manifest_data_.append(info->buffer_->data(), bytes_read);
break;
case UpdateJobInfo::URL_FETCH:
+ case UpdateJobInfo::MASTER_ENTRY_FETCH:
DCHECK(info->response_writer_.get());
info->response_writer_->WriteData(info->buffer_, bytes_read,
&info->write_callback_);
@@ -266,8 +292,6 @@ bool AppCacheUpdateJob::ConsumeResponseData(URLRequest* request,
void AppCacheUpdateJob::OnWriteResponseComplete(int result,
URLRequest* request,
UpdateJobInfo* info) {
- DCHECK(internal_state_ == DOWNLOADING);
-
if (result < 0) {
request->Cancel();
OnResponseCompleted(request);
@@ -302,6 +326,9 @@ void AppCacheUpdateJob::OnResponseCompleted(URLRequest* request) {
case UpdateJobInfo::URL_FETCH:
HandleUrlFetchCompleted(request);
break;
+ case UpdateJobInfo::MASTER_ENTRY_FETCH:
+ HandleMasterEntryFetchCompleted(request);
+ break;
case UpdateJobInfo::MANIFEST_REFETCH:
HandleManifestRefetchCompleted(request);
break;
@@ -340,6 +367,10 @@ bool AppCacheUpdateJob::RetryRequest(URLRequest* request) {
pending_url_fetches_.erase(url);
pending_url_fetches_.insert(PendingUrlFetches::value_type(url, retry));
break;
+ case UpdateJobInfo::MASTER_ENTRY_FETCH:
+ master_entry_fetches_.erase(url);
+ master_entry_fetches_.insert(PendingUrlFetches::value_type(url, retry));
+ break;
default:
NOTREACHED();
}
@@ -354,21 +385,16 @@ void AppCacheUpdateJob::HandleManifestFetchCompleted(URLRequest* request) {
DCHECK(internal_state_ == FETCH_MANIFEST);
manifest_url_request_ = NULL;
- if (!request->status().is_success()) {
- LOG(INFO) << "Request non-success, status: " << request->status().status()
- << " os_error: " << request->status().os_error();
- internal_state_ = CACHE_FAILURE;
- MaybeCompleteUpdate(); // if not done, run async cache failure steps
- return;
- }
-
- int response_code = request->GetResponseCode();
+ int response_code = -1;
std::string mime_type;
- request->GetMimeType(&mime_type);
- manifest_response_info_.reset(
- new net::HttpResponseInfo(request->response_info()));
+ if (request->status().is_success()) {
+ response_code = request->GetResponseCode();
+ request->GetMimeType(&mime_type);
+ }
if ((response_code / 100 == 2) && mime_type == kManifestMimeType) {
+ manifest_response_info_.reset(
+ new net::HttpResponseInfo(request->response_info()));
if (update_type_ == UPGRADE_ATTEMPT)
CheckIfManifestChanged(); // continues asynchronously
else
@@ -378,15 +404,16 @@ void AppCacheUpdateJob::HandleManifestFetchCompleted(URLRequest* request) {
} else if (response_code == 404 || response_code == 410) {
service_->storage()->MakeGroupObsolete(group_, this); // async
} else {
- LOG(INFO) << "Cache failure, response code: " << response_code;
internal_state_ = CACHE_FAILURE;
+ CancelAllMasterEntryFetches();
MaybeCompleteUpdate(); // if not done, run async cache failure steps
}
}
void AppCacheUpdateJob::OnGroupMadeObsolete(AppCacheGroup* group,
bool success) {
- NotifyAllPendingMasterHosts(ERROR_EVENT);
+ DCHECK(master_entry_fetches_.empty());
+ CancelAllMasterEntryFetches();
if (success) {
DCHECK(group->is_obsolete());
NotifyAllAssociatedHosts(OBSOLETE_EVENT);
@@ -404,6 +431,9 @@ void AppCacheUpdateJob::ContinueHandleManifestFetchCompleted(bool changed) {
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;
}
@@ -413,6 +443,7 @@ void AppCacheUpdateJob::ContinueHandleManifestFetchCompleted(bool changed) {
manifest_data_.length(), manifest)) {
LOG(INFO) << "Failed to parse manifest: " << manifest_url_;
internal_state_ = CACHE_FAILURE;
+ CancelAllMasterEntryFetches();
MaybeCompleteUpdate(); // if not done, run async cache failure steps
return;
}
@@ -437,6 +468,7 @@ void AppCacheUpdateJob::ContinueHandleManifestFetchCompleted(bool changed) {
group_->SetUpdateStatus(AppCacheGroup::DOWNLOADING);
NotifyAllAssociatedHosts(DOWNLOADING_EVENT);
FetchUrls();
+ FetchMasterEntries();
MaybeCompleteUpdate(); // if not done, continues when async fetches complete
}
@@ -451,15 +483,14 @@ void AppCacheUpdateJob::HandleUrlFetchCompleted(URLRequest* request) {
? request->GetResponseCode() : -1;
AppCacheEntry& entry = url_file_list_.find(url)->second;
- UpdateJobInfo* info =
- static_cast<UpdateJobInfo*>(request->GetUserData(this));
-
if (request->status().is_success() && (response_code / 100 == 2)) {
// Associate storage with the new entry.
+ UpdateJobInfo* info =
+ static_cast<UpdateJobInfo*>(request->GetUserData(this));
DCHECK(info->response_writer_.get());
entry.set_response_id(info->response_writer_->response_id());
- inprogress_cache_->AddEntry(url, entry);
+ inprogress_cache_->AddOrModifyEntry(url, entry);
// Foreign entries will be detected during cache selection.
// Note: 6.9.4, step 17.9 possible optimization: if resource is HTML or XML
@@ -476,17 +507,8 @@ void AppCacheUpdateJob::HandleUrlFetchCompleted(URLRequest* request) {
if (entry.IsExplicit() || entry.IsFallback()) {
internal_state_ = CACHE_FAILURE;
-
- // 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();
+ CancelAllUrlFetches();
+ CancelAllMasterEntryFetches();
} else if (response_code == 404 || response_code == 410) {
// Entry is skipped. They are dropped from the cache.
} else if (update_type_ == UPGRADE_ATTEMPT) {
@@ -505,6 +527,83 @@ void AppCacheUpdateJob::HandleUrlFetchCompleted(URLRequest* request) {
MaybeCompleteUpdate();
}
+void AppCacheUpdateJob::HandleMasterEntryFetchCompleted(URLRequest* request) {
+ 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.
+
+ 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.
+ UpdateJobInfo* info =
+ static_cast<UpdateJobInfo*>(request->GetUserData(this));
+ AppCache* cache = inprogress_cache_ ? inprogress_cache_.get() :
+ group_->newest_complete_cache();
+ DCHECK(info->response_writer_.get());
+ AppCacheEntry master_entry(AppCacheEntry::MASTER,
+ info->response_writer_->response_id());
+ cache->AddOrModifyEntry(url, master_entry);
+
+ // In no-update case, associate host with the newest cache.
+ if (!inprogress_cache_) {
+ DCHECK(cache == group_->newest_complete_cache());
+ for (PendingHosts::iterator host_it = hosts.begin();
+ host_it != hosts.end(); ++host_it) {
+ (*host_it)->AssociateCache(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_) {
+ host->AssociateCache(NULL);
+ }
+
+ host->RemoveObserver(this);
+ }
+ hosts.clear();
+ host_notifier.SendNotifications(ERROR_EVENT);
+
+ // In downloading case, update result is different if all master entries
+ // failed vs. only some failing.
+ if (inprogress_cache_) {
+ // 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()) {
+ CancelAllUrlFetches();
+ internal_state_ = CACHE_FAILURE;
+ }
+ }
+ }
+
+ if (internal_state_ != CACHE_FAILURE)
+ FetchMasterEntries();
+
+ MaybeCompleteUpdate();
+}
+
void AppCacheUpdateJob::HandleManifestRefetchCompleted(URLRequest* request) {
DCHECK(internal_state_ == REFETCH_MANIFEST);
manifest_url_request_ = NULL;
@@ -512,7 +611,7 @@ void AppCacheUpdateJob::HandleManifestRefetchCompleted(URLRequest* request) {
int response_code = request->status().is_success()
? request->GetResponseCode() : -1;
if (response_code == 304 || manifest_data_ == manifest_refetch_data_) {
- // Only need to store response in storage if manifest is not already an
+ // 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) {
@@ -570,11 +669,6 @@ void AppCacheUpdateJob::OnGroupAndNewestCacheStored(AppCacheGroup* group,
bool success) {
if (success) {
DCHECK_EQ(protect_new_cache_, group->newest_complete_cache());
- if (update_type_ == CACHE_ATTEMPT)
- NotifyAllAssociatedHosts(CACHED_EVENT);
- else
- NotifyAllAssociatedHosts(UPDATE_READY_EVENT);
- internal_state_ = COMPLETED;
MaybeCompleteUpdate(); // will definitely complete
} else {
protect_new_cache_ = NULL;
@@ -585,9 +679,9 @@ void AppCacheUpdateJob::OnGroupAndNewestCacheStored(AppCacheGroup* group,
}
void AppCacheUpdateJob::HandleManifestRefetchFailure() {
- ScheduleUpdateRetry(kRerunDelayMs);
- internal_state_ = CACHE_FAILURE;
- MaybeCompleteUpdate(); // will definitely complete
+ ScheduleUpdateRetry(kRerunDelayMs);
+ internal_state_ = CACHE_FAILURE;
+ MaybeCompleteUpdate(); // will definitely complete
}
void AppCacheUpdateJob::NotifySingleHost(AppCacheHost* host,
@@ -638,6 +732,17 @@ void AppCacheUpdateJob::NotifyAllAssociatedHosts(EventID event_id) {
host_notifier.SendNotifications(event_id);
}
+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::CheckIfManifestChanged() {
DCHECK(update_type_ == UPGRADE_ATTEMPT);
AppCacheEntry* entry =
@@ -724,6 +829,8 @@ void AppCacheUpdateJob::FetchUrls() {
AppCacheEntry& entry = it->second;
if (ShouldSkipUrlFetch(entry)) {
++url_fetches_completed_;
+ } else if (AlreadyFetchedEntry(url, entry.types())) {
+ ++url_fetches_completed_; // saved a URL request
} else if (!storage_checked && MaybeLoadFromNewestCache(url, entry)) {
// Continues asynchronously after data is loaded from newest cache.
} else {
@@ -739,6 +846,19 @@ void AppCacheUpdateJob::FetchUrls() {
}
}
+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) {
if (entry.IsExplicit() || entry.IsFallback()) {
return false;
@@ -748,6 +868,133 @@ bool AppCacheUpdateJob::ShouldSkipUrlFetch(const AppCacheEntry& entry) {
return false;
}
+bool AppCacheUpdateJob::AlreadyFetchedEntry(const GURL& url,
+ int entry_type) {
+ DCHECK(internal_state_ == DOWNLOADING || internal_state_ == NO_UPDATE);
+ AppCacheEntry* existing = inprogress_cache_ ?
+ 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_) {
+ host->AssociateCache(inprogress_cache_); // always associate
+ 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)
+ host->AssociateCache(cache); // only associate if have entry
+ 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) {
+ 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)->AssociateCache(cache);
+ }
+ }
+ } else {
+ // Send URL request for the master entry.
+ URLRequest* request = new URLRequest(url, this);
+ request->SetUserData(this,
+ new UpdateJobInfo(UpdateJobInfo::MASTER_ENTRY_FETCH));
+ request->set_context(service_->request_context());
+ request->set_load_flags(
+ request->load_flags() | net::LOAD_DISABLE_INTERCEPT);
+ request->Start();
+ master_entry_fetches_.insert(PendingUrlFetches::value_type(url, request));
+ }
+
+ master_entries_to_fetch_.erase(master_entries_to_fetch_.begin());
+ }
+}
+
+void AppCacheUpdateJob::CancelAllMasterEntryFetches() {
+ // 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->AssociateCache(NULL);
+ host_notifier.AddHost(host);
+ host->RemoveObserver(this);
+ }
+ hosts.clear();
+
+ master_entries_to_fetch_.erase(master_entries_to_fetch_.begin());
+ }
+ host_notifier.SendNotifications(ERROR_EVENT);
+}
+
bool AppCacheUpdateJob::MaybeLoadFromNewestCache(const GURL& url,
AppCacheEntry& entry) {
if (update_type_ != UPGRADE_ATTEMPT)
@@ -787,13 +1034,13 @@ void AppCacheUpdateJob::CopyEntryToCache(const GURL& url,
AppCacheEntry* dest) {
DCHECK(dest);
dest->set_response_id(src.response_id());
- inprogress_cache_->AddEntry(url, *dest);
+ inprogress_cache_->AddOrModifyEntry(url, *dest);
}
void AppCacheUpdateJob::MaybeCompleteUpdate() {
// 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() ) {
+ url_fetches_completed_ != url_file_list_.size()) {
DCHECK(internal_state_ != COMPLETED);
return;
}
@@ -802,17 +1049,22 @@ void AppCacheUpdateJob::MaybeCompleteUpdate() {
case NO_UPDATE:
// 6.9.4 steps 7.3-7.7.
NotifyAllAssociatedHosts(NO_UPDATE_EVENT);
- pending_master_entries_.clear();
internal_state_ = COMPLETED;
break;
case DOWNLOADING:
internal_state_ = REFETCH_MANIFEST;
FetchManifest(false);
break;
+ case REFETCH_MANIFEST:
+ if (update_type_ == CACHE_ATTEMPT)
+ NotifyAllAssociatedHosts(CACHED_EVENT);
+ else
+ NotifyAllAssociatedHosts(UPDATE_READY_EVENT);
+ internal_state_ = COMPLETED;
+ break;
case CACHE_FAILURE:
// 6.9.4 cache failure steps 2-8.
NotifyAllAssociatedHosts(ERROR_EVENT);
- pending_master_entries_.clear();
DiscardInprogressCache();
// For a CACHE_ATTEMPT, group will be discarded when the host(s) that
// started this update removes its reference to the group. Nothing more
@@ -847,8 +1099,15 @@ void AppCacheUpdateJob::Cancel() {
it != pending_url_fetches_.end(); ++it) {
delete it->second;
}
+ pending_url_fetches_.clear();
- pending_master_entries_.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.
@@ -858,10 +1117,27 @@ void AppCacheUpdateJob::Cancel() {
service_->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 (!inprogress_cache_)
return;
+ AppCache::AppCacheHosts& hosts = inprogress_cache_->associated_hosts();
+ while (!hosts.empty())
+ (*hosts.begin())->AssociateCache(NULL);
+
// TODO(jennb): Cleanup stored responses for entries in the cache?
// May not be necessary if handled automatically by storage layer.
@@ -869,6 +1145,7 @@ void AppCacheUpdateJob::DiscardInprogressCache() {
}
void AppCacheUpdateJob::DeleteSoon() {
+ ClearPendingMasterEntries();
manifest_response_writer_.reset();
service_->storage()->CancelDelegateCallbacks(this);
diff --git a/webkit/appcache/appcache_update_job.h b/webkit/appcache/appcache_update_job.h
index b60ca0b..7c60ef1 100644
--- a/webkit/appcache/appcache_update_job.h
+++ b/webkit/appcache/appcache_update_job.h
@@ -7,12 +7,16 @@
#include <deque>
#include <map>
+#include <set>
+#include <string>
+#include <vector>
#include "base/ref_counted.h"
#include "base/task.h"
#include "googleurl/src/gurl.h"
#include "net/url_request/url_request.h"
#include "webkit/appcache/appcache.h"
+#include "webkit/appcache/appcache_host.h"
#include "webkit/appcache/appcache_interfaces.h"
#include "webkit/appcache/appcache_storage.h"
@@ -22,7 +26,8 @@ class UpdateJobInfo;
// Application cache Update algorithm and state.
class AppCacheUpdateJob : public URLRequest::Delegate,
- public AppCacheStorage::Delegate {
+ public AppCacheStorage::Delegate,
+ public AppCacheHost::Observer {
public:
AppCacheUpdateJob(AppCacheService* service, AppCacheGroup* group);
~AppCacheUpdateJob();
@@ -56,6 +61,8 @@ class AppCacheUpdateJob : public URLRequest::Delegate,
FETCH_MANIFEST,
NO_UPDATE,
DOWNLOADING,
+
+ // Every state after this comment indicates the update is terminating.
REFETCH_MANIFEST,
CACHE_FAILURE,
CANCELLED,
@@ -74,6 +81,10 @@ class AppCacheUpdateJob : public URLRequest::Delegate,
void OnGroupAndNewestCacheStored(AppCacheGroup* group, bool success);
void OnGroupMadeObsolete(AppCacheGroup* group, bool success);
+ // Methods for AppCacheHost::Observer.
+ void OnCacheSelectionComplete(AppCacheHost* host) {} // N/A
+ void OnDestructionImminent(AppCacheHost* host);
+
void FetchManifest(bool is_first_fetch);
void OnResponseCompleted(URLRequest* request);
@@ -97,6 +108,7 @@ class AppCacheUpdateJob : public URLRequest::Delegate,
void ContinueHandleManifestFetchCompleted(bool changed);
void HandleUrlFetchCompleted(URLRequest* request);
+ void HandleMasterEntryFetchCompleted(URLRequest* request);
void HandleManifestRefetchCompleted(URLRequest* request);
void OnManifestInfoWriteComplete(int result);
@@ -118,8 +130,22 @@ class AppCacheUpdateJob : public URLRequest::Delegate,
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();
+
// 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
@@ -144,11 +170,14 @@ class AppCacheUpdateJob : public URLRequest::Delegate,
void ScheduleUpdateRetry(int delay_ms);
void Cancel();
+ void ClearPendingMasterEntries();
void DiscardInprogressCache();
// Deletes this object after letting the stack unwind.
void DeleteSoon();
+ bool IsTerminating() { return internal_state_ >= REFETCH_MANIFEST; }
+
// This factory will be used to schedule invocations of various methods.
ScopedRunnableMethodFactory<AppCacheUpdateJob> method_factory_;
@@ -171,6 +200,14 @@ class AppCacheUpdateJob : public URLRequest::Delegate,
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_;
diff --git a/webkit/appcache/appcache_update_job_unittest.cc b/webkit/appcache/appcache_update_job_unittest.cc
index 2f4d1b6..74b071d 100644
--- a/webkit/appcache/appcache_update_job_unittest.cc
+++ b/webkit/appcache/appcache_update_job_unittest.cc
@@ -21,6 +21,8 @@ const wchar_t kDocRoot[] = L"webkit/appcache/data/appcache_unittest";
class MockFrontend : public AppCacheFrontend {
public:
+ MockFrontend() : update_(NULL) { }
+
virtual void OnCacheSelected(int host_id, int64 cache_id,
Status status) {
}
@@ -32,12 +34,33 @@ class MockFrontend : public AppCacheFrontend {
virtual void OnEventRaised(const std::vector<int>& host_ids,
EventID event_id) {
raised_events_.push_back(RaisedEvent(host_ids, event_id));
+
+ // Trigger additional updates if requested.
+ if (event_id == start_update_trigger_ && update_) {
+ for (std::vector<AppCacheHost*>::iterator it = update_hosts_.begin();
+ it != update_hosts_.end(); ++it) {
+ AppCacheHost* host = *it;
+ update_->StartUpdate(host,
+ (host ? host->pending_master_entry_url() : GURL::EmptyGURL()));
+ }
+ update_hosts_.clear(); // only trigger once
+ }
}
void AddExpectedEvent(const std::vector<int>& host_ids, EventID event_id) {
expected_events_.push_back(RaisedEvent(host_ids, event_id));
}
+ void TriggerAdditionalUpdates(EventID trigger_event,
+ AppCacheUpdateJob* update) {
+ start_update_trigger_ = trigger_event;
+ update_ = update;
+ }
+
+ void AdditionalUpdateHost(AppCacheHost* host) {
+ update_hosts_.push_back(host);
+ }
+
typedef std::vector<int> HostIds;
typedef std::pair<HostIds, EventID> RaisedEvent;
typedef std::vector<RaisedEvent> RaisedEvents;
@@ -45,6 +68,11 @@ class MockFrontend : public AppCacheFrontend {
// Set the expected events if verification needs to happen asynchronously.
RaisedEvents expected_events_;
+
+ // Add ability for frontend to add master entries to an inprogress update.
+ EventID start_update_trigger_;
+ AppCacheUpdateJob* update_;
+ std::vector<AppCacheHost*> update_hosts_;
};
// Helper class to let us call methods of AppCacheUpdateJobTest on a
@@ -1265,6 +1293,603 @@ class AppCacheUpdateJobTest : public testing::Test,
WaitForUpdateToFinish();
}
+ void MasterEntryFetchManifestFailTest() {
+ ASSERT_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type());
+
+ MakeService();
+ group_ = new AppCacheGroup(service_.get(), GURL("http://failme"), 111);
+ AppCacheUpdateJob* update = new AppCacheUpdateJob(service_.get(), group_);
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ host->new_master_entry_url_ = GURL("http://failme/blah");
+ update->StartUpdate(host, host->new_master_entry_url_);
+ EXPECT_TRUE(update->manifest_url_request_ != NULL);
+
+ update->manifest_url_request_->SimulateError(-100);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = false;
+ MockFrontend::HostIds ids1(1, host->host_id());
+ frontend->AddExpectedEvent(ids1, CHECKING_EVENT);
+ frontend->AddExpectedEvent(ids1, ERROR_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void MasterEntryBadManifestTest() {
+ ASSERT_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type());
+
+ MakeService();
+ group_ = new AppCacheGroup(service_.get(),
+ http_server_->TestServerPage("files/bad-manifest"), 111);
+ AppCacheUpdateJob* update = new AppCacheUpdateJob(service_.get(), group_);
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ host->new_master_entry_url_ = http_server_->TestServerPage("files/blah");
+ update->StartUpdate(host, host->new_master_entry_url_);
+ EXPECT_TRUE(update->manifest_url_request_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = false;
+ MockFrontend::HostIds ids1(1, host->host_id());
+ frontend->AddExpectedEvent(ids1, CHECKING_EVENT);
+ frontend->AddExpectedEvent(ids1, ERROR_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void MasterEntryManifestNotFoundTest() {
+ ASSERT_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_.get(), http_server_->TestServerPage("files/nosuchfile"), 111);
+ AppCacheUpdateJob* update = new AppCacheUpdateJob(service_.get(), group_);
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ host->new_master_entry_url_ = http_server_->TestServerPage("files/blah");
+
+ update->StartUpdate(host, host->new_master_entry_url_);
+ EXPECT_TRUE(update->manifest_url_request_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = true;
+ expect_group_has_cache_ = false;
+ MockFrontend::HostIds ids1(1, host->host_id());
+ frontend->AddExpectedEvent(ids1, CHECKING_EVENT);
+ frontend->AddExpectedEvent(ids1, ERROR_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void MasterEntryFailUrlFetchTest() {
+ ASSERT_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type());
+
+ MakeService();
+ group_ = new AppCacheGroup(service_.get(),
+ http_server_->TestServerPage("files/manifest-fb-404"), 111);
+ AppCacheUpdateJob* update = new AppCacheUpdateJob(service_.get(), group_);
+ group_->update_job_ = update;
+
+ MockFrontend* frontend = MakeMockFrontend();
+ AppCacheHost* host = MakeHost(1, frontend);
+ host->new_master_entry_url_ =
+ http_server_->TestServerPage("files/explicit1");
+
+ update->StartUpdate(host, host->new_master_entry_url_);
+ EXPECT_TRUE(update->manifest_url_request_ != NULL);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = false; // 404 fallback url is cache failure
+ MockFrontend::HostIds ids1(1, host->host_id());
+ frontend->AddExpectedEvent(ids1, CHECKING_EVENT);
+ frontend->AddExpectedEvent(ids1, DOWNLOADING_EVENT);
+ frontend->AddExpectedEvent(ids1, PROGRESS_EVENT);
+ frontend->AddExpectedEvent(ids1, PROGRESS_EVENT);
+ frontend->AddExpectedEvent(ids1, PROGRESS_EVENT);
+ frontend->AddExpectedEvent(ids1, ERROR_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void MasterEntryAllFailTest() {
+ ASSERT_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_.get(), http_server_->TestServerPage("files/manifest1"), 111);
+ AppCacheUpdateJob* update = new AppCacheUpdateJob(service_.get(), group_);
+ group_->update_job_ = update;
+
+ MockFrontend* frontend1 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ host1->new_master_entry_url_ =
+ http_server_->TestServerPage("files/nosuchfile");
+ update->StartUpdate(host1, host1->new_master_entry_url_);
+
+ MockFrontend* frontend2 = MakeMockFrontend();
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host2->new_master_entry_url_ =
+ http_server_->TestServerPage("files/servererror");
+ update->StartUpdate(host2, host2->new_master_entry_url_);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = false; // all pending masters failed
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, DOWNLOADING_EVENT);
+ frontend1->AddExpectedEvent(ids1, PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, ERROR_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, CHECKING_EVENT);
+ frontend2->AddExpectedEvent(ids2, DOWNLOADING_EVENT);
+ frontend2->AddExpectedEvent(ids2, PROGRESS_EVENT);
+ frontend2->AddExpectedEvent(ids2, PROGRESS_EVENT);
+ frontend2->AddExpectedEvent(ids2, ERROR_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void UpgradeMasterEntryAllFailTest() {
+ ASSERT_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_.get(), http_server_->TestServerPage("files/manifest1"), 111);
+ AppCacheUpdateJob* update = new AppCacheUpdateJob(service_.get(), group_);
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 42);
+ MockFrontend* frontend1 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ host1->AssociateCache(cache);
+
+ MockFrontend* frontend2 = MakeMockFrontend();
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host2->new_master_entry_url_ =
+ http_server_->TestServerPage("files/nosuchfile");
+ update->StartUpdate(host2, host2->new_master_entry_url_);
+
+ MockFrontend* frontend3 = MakeMockFrontend();
+ AppCacheHost* host3 = MakeHost(3, frontend3);
+ host3->new_master_entry_url_ =
+ http_server_->TestServerPage("files/servererror");
+ update->StartUpdate(host3, host3->new_master_entry_url_);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_old_cache_ = cache;
+ tested_manifest_ = MANIFEST1;
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, DOWNLOADING_EVENT);
+ frontend1->AddExpectedEvent(ids1, PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, UPDATE_READY_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, DOWNLOADING_EVENT);
+ frontend2->AddExpectedEvent(ids2, PROGRESS_EVENT);
+ frontend2->AddExpectedEvent(ids2, PROGRESS_EVENT);
+ frontend2->AddExpectedEvent(ids2, ERROR_EVENT);
+ MockFrontend::HostIds ids3(1, host3->host_id());
+ frontend3->AddExpectedEvent(ids3, CHECKING_EVENT);
+ frontend3->AddExpectedEvent(ids3, DOWNLOADING_EVENT);
+ frontend3->AddExpectedEvent(ids3, PROGRESS_EVENT);
+ frontend3->AddExpectedEvent(ids3, PROGRESS_EVENT);
+ frontend3->AddExpectedEvent(ids3, ERROR_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void MasterEntrySomeFailTest() {
+ ASSERT_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_.get(), http_server_->TestServerPage("files/manifest1"), 111);
+ AppCacheUpdateJob* update = new AppCacheUpdateJob(service_.get(), group_);
+ group_->update_job_ = update;
+
+ MockFrontend* frontend1 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ host1->new_master_entry_url_ =
+ http_server_->TestServerPage("files/nosuchfile");
+ update->StartUpdate(host1, host1->new_master_entry_url_);
+
+ MockFrontend* frontend2 = MakeMockFrontend();
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host2->new_master_entry_url_ =
+ http_server_->TestServerPage("files/explicit2");
+ update->StartUpdate(host2, host2->new_master_entry_url_);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true; // as long as one pending master succeeds
+ tested_manifest_ = MANIFEST1;
+ expect_extra_entries_.insert(AppCache::EntryMap::value_type(
+ http_server_->TestServerPage("files/explicit2"),
+ AppCacheEntry(AppCacheEntry::MASTER)));
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, DOWNLOADING_EVENT);
+ frontend1->AddExpectedEvent(ids1, PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, ERROR_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, CHECKING_EVENT);
+ frontend2->AddExpectedEvent(ids2, DOWNLOADING_EVENT);
+ frontend2->AddExpectedEvent(ids2, PROGRESS_EVENT);
+ frontend2->AddExpectedEvent(ids2, PROGRESS_EVENT);
+ frontend2->AddExpectedEvent(ids2, CACHED_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void UpgradeMasterEntrySomeFailTest() {
+ ASSERT_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_.get(), http_server_->TestServerPage("files/manifest1"), 111);
+ AppCacheUpdateJob* update = new AppCacheUpdateJob(service_.get(), group_);
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 42);
+ MockFrontend* frontend1 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ host1->AssociateCache(cache);
+
+ MockFrontend* frontend2 = MakeMockFrontend();
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host2->new_master_entry_url_ =
+ http_server_->TestServerPage("files/nosuchfile");
+ update->StartUpdate(host2, host2->new_master_entry_url_);
+
+ MockFrontend* frontend3 = MakeMockFrontend();
+ AppCacheHost* host3 = MakeHost(3, frontend3);
+ host3->new_master_entry_url_ =
+ http_server_->TestServerPage("files/explicit2");
+ update->StartUpdate(host3, host3->new_master_entry_url_);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_old_cache_ = cache;
+ tested_manifest_ = MANIFEST1;
+ expect_extra_entries_.insert(AppCache::EntryMap::value_type(
+ http_server_->TestServerPage("files/explicit2"),
+ AppCacheEntry(AppCacheEntry::MASTER)));
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, DOWNLOADING_EVENT);
+ frontend1->AddExpectedEvent(ids1, PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, UPDATE_READY_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, DOWNLOADING_EVENT);
+ frontend2->AddExpectedEvent(ids2, PROGRESS_EVENT);
+ frontend2->AddExpectedEvent(ids2, PROGRESS_EVENT);
+ frontend2->AddExpectedEvent(ids2, ERROR_EVENT);
+ MockFrontend::HostIds ids3(1, host3->host_id());
+ frontend3->AddExpectedEvent(ids3, CHECKING_EVENT);
+ frontend3->AddExpectedEvent(ids3, DOWNLOADING_EVENT);
+ frontend3->AddExpectedEvent(ids3, PROGRESS_EVENT);
+ frontend3->AddExpectedEvent(ids3, PROGRESS_EVENT);
+ frontend3->AddExpectedEvent(ids3, UPDATE_READY_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void MasterEntryNoUpdateTest() {
+ ASSERT_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type());
+
+ MakeService();
+ group_ = new AppCacheGroup(service_.get(),
+ http_server_->TestServerPage("files/notmodified"), 111);
+ AppCacheUpdateJob* update = new AppCacheUpdateJob(service_.get(), group_);
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(1, 111);
+ MockFrontend* frontend1 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ host1->AssociateCache(cache);
+
+ // Give cache an existing entry that can also be fetched.
+ cache->AddEntry(http_server_->TestServerPage("files/explicit2"),
+ AppCacheEntry(AppCacheEntry::EXPLICIT, 222));
+
+ MockFrontend* frontend2 = MakeMockFrontend();
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host2->new_master_entry_url_ =
+ http_server_->TestServerPage("files/explicit1");
+ update->StartUpdate(host2, host2->new_master_entry_url_);
+
+ AppCacheHost* host3 = MakeHost(3, frontend2); // same frontend as host2
+ host3->new_master_entry_url_ =
+ http_server_->TestServerPage("files/explicit2");
+ update->StartUpdate(host3, host3->new_master_entry_url_);
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_newest_cache_ = cache; // newest cache still the same cache
+ tested_manifest_ = PENDING_MASTER_NO_UPDATE;
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, NO_UPDATE_EVENT);
+ MockFrontend::HostIds ids3(1, host3->host_id());
+ frontend2->AddExpectedEvent(ids3, CHECKING_EVENT);
+ MockFrontend::HostIds ids2and3;
+ ids2and3.push_back(host2->host_id());
+ ids2and3.push_back(host3->host_id());
+ frontend2->AddExpectedEvent(ids2and3, NO_UPDATE_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void StartUpdateMidCacheAttemptTest() {
+ ASSERT_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_.get(), http_server_->TestServerPage("files/manifest1"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update = new AppCacheUpdateJob(service_.get(), group_);
+ group_->update_job_ = update;
+
+ MockFrontend* frontend1 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ host1->new_master_entry_url_ =
+ http_server_->TestServerPage("files/explicit2");
+ update->StartUpdate(host1, host1->new_master_entry_url_);
+ EXPECT_TRUE(update->manifest_url_request_ != NULL);
+
+ // Set up additional updates to be started while update is in progress.
+ MockFrontend* frontend2 = MakeMockFrontend();
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host2->new_master_entry_url_ =
+ http_server_->TestServerPage("files/nosuchfile");
+
+ MockFrontend* frontend3 = MakeMockFrontend();
+ AppCacheHost* host3 = MakeHost(3, frontend3);
+ host3->new_master_entry_url_ =
+ http_server_->TestServerPage("files/explicit1");
+
+ MockFrontend* frontend4 = MakeMockFrontend();
+ AppCacheHost* host4 = MakeHost(4, frontend4);
+ host4->new_master_entry_url_ =
+ http_server_->TestServerPage("files/explicit2");
+
+ MockFrontend* frontend5 = MakeMockFrontend();
+ AppCacheHost* host5 = MakeHost(5, frontend5); // no master entry url
+
+ frontend1->TriggerAdditionalUpdates(DOWNLOADING_EVENT, update);
+ frontend1->AdditionalUpdateHost(host2); // fetch will fail
+ frontend1->AdditionalUpdateHost(host3); // same as an explicit entry
+ frontend1->AdditionalUpdateHost(host4); // same as another master entry
+ frontend1->AdditionalUpdateHost(NULL); // no host
+ frontend1->AdditionalUpdateHost(host5); // no master entry url
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ tested_manifest_ = MANIFEST1;
+ expect_extra_entries_.insert(AppCache::EntryMap::value_type(
+ http_server_->TestServerPage("files/explicit2"),
+ AppCacheEntry(AppCacheEntry::MASTER)));
+ MockFrontend::HostIds ids1(1, host1->host_id());
+ frontend1->AddExpectedEvent(ids1, CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, DOWNLOADING_EVENT);
+ frontend1->AddExpectedEvent(ids1, PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, CACHED_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, CHECKING_EVENT);
+ frontend2->AddExpectedEvent(ids2, DOWNLOADING_EVENT);
+ frontend2->AddExpectedEvent(ids2, PROGRESS_EVENT);
+ frontend2->AddExpectedEvent(ids2, PROGRESS_EVENT);
+ frontend2->AddExpectedEvent(ids2, ERROR_EVENT);
+ MockFrontend::HostIds ids3(1, host3->host_id());
+ frontend3->AddExpectedEvent(ids3, CHECKING_EVENT);
+ frontend3->AddExpectedEvent(ids3, DOWNLOADING_EVENT);
+ frontend3->AddExpectedEvent(ids3, PROGRESS_EVENT);
+ frontend3->AddExpectedEvent(ids3, PROGRESS_EVENT);
+ frontend3->AddExpectedEvent(ids3, CACHED_EVENT);
+ MockFrontend::HostIds ids4(1, host4->host_id());
+ frontend4->AddExpectedEvent(ids4, CHECKING_EVENT);
+ frontend4->AddExpectedEvent(ids4, DOWNLOADING_EVENT);
+ frontend4->AddExpectedEvent(ids4, PROGRESS_EVENT);
+ frontend4->AddExpectedEvent(ids4, PROGRESS_EVENT);
+ frontend4->AddExpectedEvent(ids4, CACHED_EVENT);
+
+ // Host 5 is not associated with cache so no progress/cached events.
+ MockFrontend::HostIds ids5(1, host5->host_id());
+ frontend5->AddExpectedEvent(ids5, CHECKING_EVENT);
+ frontend5->AddExpectedEvent(ids5, DOWNLOADING_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void StartUpdateMidNoUpdateTest() {
+ ASSERT_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_.get(), http_server_->TestServerPage("files/notmodified"),
+ service_->storage()->NewGroupId());
+ AppCacheUpdateJob* update = new AppCacheUpdateJob(service_.get(), group_);
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(1, 111);
+ MockFrontend* frontend1 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ host1->AssociateCache(cache);
+
+ // Give cache an existing entry.
+ cache->AddEntry(http_server_->TestServerPage("files/explicit2"),
+ AppCacheEntry(AppCacheEntry::EXPLICIT, 222));
+
+ // Start update with a pending master entry that will fail to give us an
+ // event to trigger other updates.
+ MockFrontend* frontend2 = MakeMockFrontend();
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host2->new_master_entry_url_ =
+ http_server_->TestServerPage("files/nosuchfile");
+ update->StartUpdate(host2, host2->new_master_entry_url_);
+ EXPECT_TRUE(update->manifest_url_request_ != NULL);
+
+ // Set up additional updates to be started while update is in progress.
+ MockFrontend* frontend3 = MakeMockFrontend();
+ AppCacheHost* host3 = MakeHost(3, frontend3);
+ host3->new_master_entry_url_ =
+ http_server_->TestServerPage("files/explicit1");
+
+ MockFrontend* frontend4 = MakeMockFrontend();
+ AppCacheHost* host4 = MakeHost(4, frontend4); // no master entry url
+
+ MockFrontend* frontend5 = MakeMockFrontend();
+ AppCacheHost* host5 = MakeHost(5, frontend5);
+ host5->new_master_entry_url_ =
+ http_server_->TestServerPage("files/explicit2"); // existing entry
+
+ MockFrontend* frontend6 = MakeMockFrontend();
+ AppCacheHost* host6 = MakeHost(6, frontend6);
+ host6->new_master_entry_url_ =
+ http_server_->TestServerPage("files/explicit1");
+
+ frontend2->TriggerAdditionalUpdates(ERROR_EVENT, update);
+ frontend2->AdditionalUpdateHost(host3);
+ frontend2->AdditionalUpdateHost(NULL); // no host
+ frontend2->AdditionalUpdateHost(host4); // no master entry url
+ frontend2->AdditionalUpdateHost(host5); // same as existing cache entry
+ frontend2->AdditionalUpdateHost(host6); // same as another master entry
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ expect_newest_cache_ = cache; // newest cache unaffected by update
+ tested_manifest_ = PENDING_MASTER_NO_UPDATE;
+ MockFrontend::HostIds ids1(1, host1->host_id()); // prior associated host
+ frontend1->AddExpectedEvent(ids1, CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, NO_UPDATE_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, ERROR_EVENT);
+ MockFrontend::HostIds ids3(1, host3->host_id());
+ frontend3->AddExpectedEvent(ids3, CHECKING_EVENT);
+ frontend3->AddExpectedEvent(ids3, NO_UPDATE_EVENT);
+ MockFrontend::HostIds ids4(1, host4->host_id()); // unassociated w/cache
+ frontend4->AddExpectedEvent(ids4, CHECKING_EVENT);
+ MockFrontend::HostIds ids5(1, host5->host_id());
+ frontend5->AddExpectedEvent(ids5, CHECKING_EVENT);
+ frontend5->AddExpectedEvent(ids5, NO_UPDATE_EVENT);
+ MockFrontend::HostIds ids6(1, host6->host_id());
+ frontend6->AddExpectedEvent(ids6, CHECKING_EVENT);
+ frontend6->AddExpectedEvent(ids6, NO_UPDATE_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
+ void StartUpdateMidDownloadTest() {
+ ASSERT_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type());
+
+ MakeService();
+ group_ = new AppCacheGroup(
+ service_.get(), http_server_->TestServerPage("files/manifest1"), 111);
+ AppCacheUpdateJob* update = new AppCacheUpdateJob(service_.get(), group_);
+ group_->update_job_ = update;
+
+ AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), 42);
+ MockFrontend* frontend1 = MakeMockFrontend();
+ AppCacheHost* host1 = MakeHost(1, frontend1);
+ host1->AssociateCache(cache);
+
+ update->StartUpdate(NULL, GURL::EmptyGURL());
+
+ // Set up additional updates to be started while update is in progress.
+ MockFrontend* frontend2 = MakeMockFrontend();
+ AppCacheHost* host2 = MakeHost(2, frontend2);
+ host2->new_master_entry_url_ =
+ http_server_->TestServerPage("files/explicit1");
+
+ MockFrontend* frontend3 = MakeMockFrontend();
+ AppCacheHost* host3 = MakeHost(3, frontend3);
+ host3->new_master_entry_url_ =
+ http_server_->TestServerPage("files/explicit2");
+
+ MockFrontend* frontend4 = MakeMockFrontend();
+ AppCacheHost* host4 = MakeHost(4, frontend4); // no master entry url
+
+ MockFrontend* frontend5 = MakeMockFrontend();
+ AppCacheHost* host5 = MakeHost(5, frontend5);
+ host5->new_master_entry_url_ =
+ http_server_->TestServerPage("files/explicit2");
+
+ frontend1->TriggerAdditionalUpdates(PROGRESS_EVENT, update);
+ frontend1->AdditionalUpdateHost(host2); // same as entry in manifest
+ frontend1->AdditionalUpdateHost(NULL); // no host
+ frontend1->AdditionalUpdateHost(host3); // new master entry
+ frontend1->AdditionalUpdateHost(host4); // no master entry url
+ frontend1->AdditionalUpdateHost(host5); // same as another master entry
+
+ // Set up checks for when update job finishes.
+ do_checks_after_update_finished_ = true;
+ expect_group_obsolete_ = false;
+ expect_group_has_cache_ = true;
+ tested_manifest_ = MANIFEST1;
+ expect_extra_entries_.insert(AppCache::EntryMap::value_type(
+ http_server_->TestServerPage("files/explicit2"),
+ AppCacheEntry(AppCacheEntry::MASTER)));
+ MockFrontend::HostIds ids1(1, host1->host_id()); // prior associated host
+ frontend1->AddExpectedEvent(ids1, CHECKING_EVENT);
+ frontend1->AddExpectedEvent(ids1, DOWNLOADING_EVENT);
+ frontend1->AddExpectedEvent(ids1, PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, PROGRESS_EVENT);
+ frontend1->AddExpectedEvent(ids1, UPDATE_READY_EVENT);
+ MockFrontend::HostIds ids2(1, host2->host_id());
+ frontend2->AddExpectedEvent(ids2, CHECKING_EVENT);
+ frontend2->AddExpectedEvent(ids2, DOWNLOADING_EVENT);
+ frontend2->AddExpectedEvent(ids2, PROGRESS_EVENT);
+ frontend2->AddExpectedEvent(ids2, UPDATE_READY_EVENT);
+ MockFrontend::HostIds ids3(1, host3->host_id());
+ frontend3->AddExpectedEvent(ids3, CHECKING_EVENT);
+ frontend3->AddExpectedEvent(ids3, DOWNLOADING_EVENT);
+ frontend3->AddExpectedEvent(ids3, PROGRESS_EVENT);
+ frontend3->AddExpectedEvent(ids3, UPDATE_READY_EVENT);
+ MockFrontend::HostIds ids4(1, host4->host_id()); // unassociated w/cache
+ frontend4->AddExpectedEvent(ids4, CHECKING_EVENT);
+ frontend4->AddExpectedEvent(ids4, DOWNLOADING_EVENT);
+ MockFrontend::HostIds ids5(1, host5->host_id());
+ frontend5->AddExpectedEvent(ids5, CHECKING_EVENT);
+ frontend5->AddExpectedEvent(ids5, DOWNLOADING_EVENT);
+ frontend5->AddExpectedEvent(ids5, PROGRESS_EVENT);
+ frontend5->AddExpectedEvent(ids5, UPDATE_READY_EVENT);
+
+ WaitForUpdateToFinish();
+ }
+
void WaitForUpdateToFinish() {
if (group_->update_status() == AppCacheGroup::IDLE)
UpdateFinished();
@@ -1404,30 +2029,36 @@ class AppCacheUpdateJobTest : public testing::Test,
// Verify expected cache contents last as some checks are asserts
// and will abort the test if they fail.
- switch (tested_manifest_) {
- case MANIFEST1:
- VerifyManifest1(group_->newest_complete_cache());
- break;
- case MANIFEST_MERGED_TYPES:
- VerifyManifestMergedTypes(group_->newest_complete_cache());
- break;
- case EMPTY_MANIFEST:
- VerifyEmptyManifest(group_->newest_complete_cache());
- break;
- case EMPTY_FILE_MANIFEST:
- VerifyEmptyFileManifest(group_->newest_complete_cache());
- break;
- case NONE:
- default:
- break;
+ if (tested_manifest_) {
+ AppCache* cache = group_->newest_complete_cache();
+ ASSERT_TRUE(cache != NULL);
+ EXPECT_EQ(group_, cache->owning_group());
+ EXPECT_TRUE(cache->is_complete());
+
+ switch (tested_manifest_) {
+ case MANIFEST1:
+ VerifyManifest1(cache);
+ break;
+ case MANIFEST_MERGED_TYPES:
+ VerifyManifestMergedTypes(cache);
+ break;
+ case EMPTY_MANIFEST:
+ VerifyEmptyManifest(cache);
+ break;
+ case EMPTY_FILE_MANIFEST:
+ VerifyEmptyFileManifest(cache);
+ break;
+ case PENDING_MASTER_NO_UPDATE:
+ VerifyMasterEntryNoUpdate(cache);
+ break;
+ case NONE:
+ default:
+ break;
+ }
}
}
void VerifyManifest1(AppCache* cache) {
- ASSERT_TRUE(cache != NULL);
- EXPECT_EQ(group_, cache->owning_group());
- EXPECT_TRUE(cache->is_complete());
-
size_t expected = 3 + expect_extra_entries_.size();
EXPECT_EQ(expected, cache->entries().size());
AppCacheEntry* entry =
@@ -1436,7 +2067,7 @@ class AppCacheUpdateJobTest : public testing::Test,
EXPECT_EQ(AppCacheEntry::MANIFEST, entry->types());
entry = cache->GetEntry(http_server_->TestServerPage("files/explicit1"));
ASSERT_TRUE(entry);
- EXPECT_EQ(AppCacheEntry::EXPLICIT, entry->types());
+ EXPECT_TRUE(entry->IsExplicit());
entry = cache->GetEntry(
http_server_->TestServerPage("files/fallback1a"));
ASSERT_TRUE(entry);
@@ -1466,10 +2097,6 @@ class AppCacheUpdateJobTest : public testing::Test,
}
void VerifyManifestMergedTypes(AppCache* cache) {
- ASSERT_TRUE(cache != NULL);
- EXPECT_EQ(group_, cache->owning_group());
- EXPECT_TRUE(cache->is_complete());
-
size_t expected = 2;
EXPECT_EQ(expected, cache->entries().size());
AppCacheEntry* entry = cache->GetEntry(
@@ -1502,10 +2129,6 @@ class AppCacheUpdateJobTest : public testing::Test,
}
void VerifyEmptyManifest(AppCache* cache) {
- ASSERT_TRUE(cache != NULL);
- EXPECT_EQ(group_, cache->owning_group());
- EXPECT_TRUE(cache->is_complete());
-
size_t expected = 1;
EXPECT_EQ(expected, cache->entries().size());
AppCacheEntry* entry = cache->GetEntry(
@@ -1521,10 +2144,6 @@ class AppCacheUpdateJobTest : public testing::Test,
}
void VerifyEmptyFileManifest(AppCache* cache) {
- ASSERT_TRUE(cache != NULL);
- EXPECT_EQ(group_, cache->owning_group());
- EXPECT_TRUE(cache->is_complete());
-
EXPECT_EQ(size_t(2), cache->entries().size());
AppCacheEntry* entry = cache->GetEntry(
http_server_->TestServerPage("files/empty-file-manifest"));
@@ -1544,6 +2163,32 @@ class AppCacheUpdateJobTest : public testing::Test,
EXPECT_TRUE(cache->update_time_ > base::TimeTicks());
}
+ void VerifyMasterEntryNoUpdate(AppCache* cache) {
+ EXPECT_EQ(size_t(3), cache->entries().size());
+ AppCacheEntry* entry = cache->GetEntry(
+ http_server_->TestServerPage("files/notmodified"));
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(AppCacheEntry::MANIFEST, entry->types());
+
+ entry = cache->GetEntry(
+ http_server_->TestServerPage("files/explicit1"));
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(AppCacheEntry::MASTER, entry->types());
+ EXPECT_TRUE(entry->has_response_id());
+
+ entry = cache->GetEntry(
+ http_server_->TestServerPage("files/explicit2"));
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(AppCacheEntry::EXPLICIT | AppCacheEntry::MASTER, entry->types());
+ EXPECT_TRUE(entry->has_response_id());
+
+ EXPECT_TRUE(cache->fallback_namespaces_.empty());
+ EXPECT_TRUE(cache->online_whitelist_namespaces_.empty());
+ EXPECT_FALSE(cache->online_whitelist_all_);
+
+ EXPECT_TRUE(cache->update_time_ > base::TimeTicks());
+ }
+
private:
// Various manifest files used in this test.
enum TestedManifest {
@@ -1552,6 +2197,7 @@ class AppCacheUpdateJobTest : public testing::Test,
MANIFEST_MERGED_TYPES,
EMPTY_MANIFEST,
EMPTY_FILE_MANIFEST,
+ PENDING_MASTER_NO_UPDATE,
};
static scoped_ptr<base::Thread> io_thread_;
@@ -1764,4 +2410,52 @@ TEST_F(AppCacheUpdateJobTest, UpgradeFailMakeGroupObsolete) {
RunTestOnIOThread(&AppCacheUpdateJobTest::UpgradeFailMakeGroupObsoleteTest);
}
+TEST_F(AppCacheUpdateJobTest, MasterEntryFetchManifestFail) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::MasterEntryFetchManifestFailTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, MasterEntryBadManifest) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::MasterEntryBadManifestTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, MasterEntryManifestNotFound) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::MasterEntryManifestNotFoundTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, MasterEntryFailUrlFetch) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::MasterEntryFailUrlFetchTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, MasterEntryAllFail) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::MasterEntryAllFailTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, UpgradeMasterEntryAllFail) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::UpgradeMasterEntryAllFailTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, MasterEntrySomeFail) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::MasterEntrySomeFailTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, UpgradeMasterEntrySomeFail) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::UpgradeMasterEntrySomeFailTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, MasterEntryNoUpdate) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::MasterEntryNoUpdateTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, StartUpdateMidCacheAttempt) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::StartUpdateMidCacheAttemptTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, StartUpdateMidNoUpdate) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::StartUpdateMidNoUpdateTest);
+}
+
+TEST_F(AppCacheUpdateJobTest, StartUpdateMidDownload) {
+ RunTestOnIOThread(&AppCacheUpdateJobTest::StartUpdateMidDownloadTest);
+}
+
} // namespace appcache
diff --git a/webkit/appcache/data/appcache_unittest/bad-manifest b/webkit/appcache/data/appcache_unittest/bad-manifest
new file mode 100644
index 0000000..6a7d6e2
--- /dev/null
+++ b/webkit/appcache/data/appcache_unittest/bad-manifest
@@ -0,0 +1,2 @@
+BAD CACHE MANIFEST
+
diff --git a/webkit/appcache/data/appcache_unittest/bad-manifest.mock-http-headers b/webkit/appcache/data/appcache_unittest/bad-manifest.mock-http-headers
new file mode 100644
index 0000000..6e904b9
--- /dev/null
+++ b/webkit/appcache/data/appcache_unittest/bad-manifest.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 200 OK
+Content-type: text/cache-manifest