Dismiss autofill popup on screen orientation change.
[chromium-blink-merge.git] / webkit / browser / appcache / appcache_update_job.cc
blob4efe947af845e32051033d6640e9cf37cea0e7b3
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "webkit/browser/appcache/appcache_update_job.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/compiler_specific.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "net/base/io_buffer.h"
14 #include "net/base/load_flags.h"
15 #include "net/base/net_errors.h"
16 #include "net/http/http_request_headers.h"
17 #include "net/http/http_response_headers.h"
18 #include "net/url_request/url_request_context.h"
19 #include "webkit/browser/appcache/appcache_group.h"
20 #include "webkit/browser/appcache/appcache_histograms.h"
22 namespace appcache {
24 static const int kBufferSize = 32768;
25 static const size_t kMaxConcurrentUrlFetches = 2;
26 static const int kMax503Retries = 3;
28 // Helper class for collecting hosts per frontend when sending notifications
29 // so that only one notification is sent for all hosts using the same frontend.
30 class HostNotifier {
31 public:
32 typedef std::vector<int> HostIds;
33 typedef std::map<AppCacheFrontend*, HostIds> NotifyHostMap;
35 // Caller is responsible for ensuring there will be no duplicate hosts.
36 void AddHost(AppCacheHost* host) {
37 std::pair<NotifyHostMap::iterator , bool> ret = hosts_to_notify.insert(
38 NotifyHostMap::value_type(host->frontend(), HostIds()));
39 ret.first->second.push_back(host->host_id());
42 void AddHosts(const std::set<AppCacheHost*>& hosts) {
43 for (std::set<AppCacheHost*>::const_iterator it = hosts.begin();
44 it != hosts.end(); ++it) {
45 AddHost(*it);
49 void SendNotifications(EventID event_id) {
50 for (NotifyHostMap::iterator it = hosts_to_notify.begin();
51 it != hosts_to_notify.end(); ++it) {
52 AppCacheFrontend* frontend = it->first;
53 frontend->OnEventRaised(it->second, event_id);
57 void SendProgressNotifications(
58 const GURL& url, int num_total, int num_complete) {
59 for (NotifyHostMap::iterator it = hosts_to_notify.begin();
60 it != hosts_to_notify.end(); ++it) {
61 AppCacheFrontend* frontend = it->first;
62 frontend->OnProgressEventRaised(it->second, url,
63 num_total, num_complete);
67 void SendErrorNotifications(const std::string& error_message) {
68 DCHECK(!error_message.empty());
69 for (NotifyHostMap::iterator it = hosts_to_notify.begin();
70 it != hosts_to_notify.end(); ++it) {
71 AppCacheFrontend* frontend = it->first;
72 frontend->OnErrorEventRaised(it->second, error_message);
76 private:
77 NotifyHostMap hosts_to_notify;
80 AppCacheUpdateJob::UrlToFetch::UrlToFetch(const GURL& url,
81 bool checked,
82 AppCacheResponseInfo* info)
83 : url(url),
84 storage_checked(checked),
85 existing_response_info(info) {
88 AppCacheUpdateJob::UrlToFetch::~UrlToFetch() {
91 // Helper class to fetch resources. Depending on the fetch type,
92 // can either fetch to an in-memory string or write the response
93 // data out to the disk cache.
94 AppCacheUpdateJob::URLFetcher::URLFetcher(
95 const GURL& url, FetchType fetch_type, AppCacheUpdateJob* job)
96 : url_(url),
97 job_(job),
98 fetch_type_(fetch_type),
99 retry_503_attempts_(0),
100 buffer_(new net::IOBuffer(kBufferSize)),
101 request_(job->service_->request_context()->CreateRequest(url, this)) {
104 AppCacheUpdateJob::URLFetcher::~URLFetcher() {
107 void AppCacheUpdateJob::URLFetcher::Start() {
108 request_->set_first_party_for_cookies(job_->manifest_url_);
109 request_->set_load_flags(request_->load_flags() |
110 net::LOAD_DISABLE_INTERCEPT);
111 if (existing_response_headers_.get())
112 AddConditionalHeaders(existing_response_headers_.get());
113 request_->Start();
116 void AppCacheUpdateJob::URLFetcher::OnReceivedRedirect(
117 net::URLRequest* request, const GURL& new_url, bool* defer_redirect) {
118 DCHECK(request_ == request);
119 // Redirect is not allowed by the update process.
120 request->Cancel();
121 OnResponseCompleted();
124 void AppCacheUpdateJob::URLFetcher::OnResponseStarted(
125 net::URLRequest *request) {
126 DCHECK(request == request_);
127 if (request->status().is_success() &&
128 (request->GetResponseCode() / 100) == 2) {
130 // See http://code.google.com/p/chromium/issues/detail?id=69594
131 // We willfully violate the HTML5 spec at this point in order
132 // to support the appcaching of cross-origin HTTPS resources.
133 // We've opted for a milder constraint and allow caching unless
134 // the resource has a "no-store" header. A spec change has been
135 // requested on the whatwg list.
136 // TODO(michaeln): Consider doing this for cross-origin HTTP resources too.
137 if (url_.SchemeIsSecure() &&
138 url_.GetOrigin() != job_->manifest_url_.GetOrigin()) {
139 if (request->response_headers()->
140 HasHeaderValue("cache-control", "no-store")) {
141 request->Cancel();
142 OnResponseCompleted();
143 return;
147 // Write response info to storage for URL fetches. Wait for async write
148 // completion before reading any response data.
149 if (fetch_type_ == URL_FETCH || fetch_type_ == MASTER_ENTRY_FETCH) {
150 response_writer_.reset(job_->CreateResponseWriter());
151 scoped_refptr<HttpResponseInfoIOBuffer> io_buffer(
152 new HttpResponseInfoIOBuffer(
153 new net::HttpResponseInfo(request->response_info())));
154 response_writer_->WriteInfo(
155 io_buffer.get(),
156 base::Bind(&URLFetcher::OnWriteComplete, base::Unretained(this)));
157 } else {
158 ReadResponseData();
160 } else {
161 OnResponseCompleted();
165 void AppCacheUpdateJob::URLFetcher::OnReadCompleted(
166 net::URLRequest* request, int bytes_read) {
167 DCHECK(request_ == request);
168 bool data_consumed = true;
169 if (request->status().is_success() && bytes_read > 0) {
170 data_consumed = ConsumeResponseData(bytes_read);
171 if (data_consumed) {
172 bytes_read = 0;
173 while (request->Read(buffer_.get(), kBufferSize, &bytes_read)) {
174 if (bytes_read > 0) {
175 data_consumed = ConsumeResponseData(bytes_read);
176 if (!data_consumed)
177 break; // wait for async data processing, then read more
178 } else {
179 break;
184 if (data_consumed && !request->status().is_io_pending())
185 OnResponseCompleted();
188 void AppCacheUpdateJob::URLFetcher::AddConditionalHeaders(
189 const net::HttpResponseHeaders* headers) {
190 DCHECK(request_.get() && headers);
191 net::HttpRequestHeaders extra_headers;
193 // Add If-Modified-Since header if response info has Last-Modified header.
194 const std::string last_modified = "Last-Modified";
195 std::string last_modified_value;
196 headers->EnumerateHeader(NULL, last_modified, &last_modified_value);
197 if (!last_modified_value.empty()) {
198 extra_headers.SetHeader(net::HttpRequestHeaders::kIfModifiedSince,
199 last_modified_value);
202 // Add If-None-Match header if response info has ETag header.
203 const std::string etag = "ETag";
204 std::string etag_value;
205 headers->EnumerateHeader(NULL, etag, &etag_value);
206 if (!etag_value.empty()) {
207 extra_headers.SetHeader(net::HttpRequestHeaders::kIfNoneMatch,
208 etag_value);
210 if (!extra_headers.IsEmpty())
211 request_->SetExtraRequestHeaders(extra_headers);
214 void AppCacheUpdateJob::URLFetcher::OnWriteComplete(int result) {
215 if (result < 0) {
216 request_->Cancel();
217 OnResponseCompleted();
218 return;
220 ReadResponseData();
223 void AppCacheUpdateJob::URLFetcher::ReadResponseData() {
224 InternalUpdateState state = job_->internal_state_;
225 if (state == CACHE_FAILURE || state == CANCELLED || state == COMPLETED)
226 return;
227 int bytes_read = 0;
228 request_->Read(buffer_.get(), kBufferSize, &bytes_read);
229 OnReadCompleted(request_.get(), bytes_read);
232 // Returns false if response data is processed asynchronously, in which
233 // case ReadResponseData will be invoked when it is safe to continue
234 // reading more response data from the request.
235 bool AppCacheUpdateJob::URLFetcher::ConsumeResponseData(int bytes_read) {
236 DCHECK_GT(bytes_read, 0);
237 switch (fetch_type_) {
238 case MANIFEST_FETCH:
239 case MANIFEST_REFETCH:
240 manifest_data_.append(buffer_->data(), bytes_read);
241 break;
242 case URL_FETCH:
243 case MASTER_ENTRY_FETCH:
244 DCHECK(response_writer_.get());
245 response_writer_->WriteData(
246 buffer_.get(),
247 bytes_read,
248 base::Bind(&URLFetcher::OnWriteComplete, base::Unretained(this)));
249 return false; // wait for async write completion to continue reading
250 default:
251 NOTREACHED();
253 return true;
256 void AppCacheUpdateJob::URLFetcher::OnResponseCompleted() {
257 // Retry for 503s where retry-after is 0.
258 if (request_->status().is_success() &&
259 request_->GetResponseCode() == 503 &&
260 MaybeRetryRequest()) {
261 return;
264 switch (fetch_type_) {
265 case MANIFEST_FETCH:
266 job_->HandleManifestFetchCompleted(this);
267 break;
268 case URL_FETCH:
269 job_->HandleUrlFetchCompleted(this);
270 break;
271 case MASTER_ENTRY_FETCH:
272 job_->HandleMasterEntryFetchCompleted(this);
273 break;
274 case MANIFEST_REFETCH:
275 job_->HandleManifestRefetchCompleted(this);
276 break;
277 default:
278 NOTREACHED();
281 delete this;
284 bool AppCacheUpdateJob::URLFetcher::MaybeRetryRequest() {
285 if (retry_503_attempts_ >= kMax503Retries ||
286 !request_->response_headers()->HasHeaderValue("retry-after", "0")) {
287 return false;
289 ++retry_503_attempts_;
290 request_.reset(job_->service_->request_context()->CreateRequest(url_, this));
291 Start();
292 return true;
295 AppCacheUpdateJob::AppCacheUpdateJob(AppCacheService* service,
296 AppCacheGroup* group)
297 : service_(service),
298 manifest_url_(group->manifest_url()),
299 group_(group),
300 update_type_(UNKNOWN_TYPE),
301 internal_state_(FETCH_MANIFEST),
302 master_entries_completed_(0),
303 url_fetches_completed_(0),
304 manifest_fetcher_(NULL),
305 stored_state_(UNSTORED) {
308 AppCacheUpdateJob::~AppCacheUpdateJob() {
309 if (internal_state_ != COMPLETED)
310 Cancel();
312 DCHECK(!manifest_fetcher_);
313 DCHECK(pending_url_fetches_.empty());
314 DCHECK(!inprogress_cache_.get());
315 DCHECK(pending_master_entries_.empty());
316 DCHECK(master_entry_fetches_.empty());
318 if (group_)
319 group_->SetUpdateStatus(AppCacheGroup::IDLE);
322 void AppCacheUpdateJob::StartUpdate(AppCacheHost* host,
323 const GURL& new_master_resource) {
324 DCHECK(group_->update_job() == this);
325 DCHECK(!group_->is_obsolete());
327 bool is_new_pending_master_entry = false;
328 if (!new_master_resource.is_empty()) {
329 DCHECK(new_master_resource == host->pending_master_entry_url());
330 DCHECK(!new_master_resource.has_ref());
331 DCHECK(new_master_resource.GetOrigin() == manifest_url_.GetOrigin());
333 // Cannot add more to this update if already terminating.
334 if (IsTerminating()) {
335 group_->QueueUpdate(host, new_master_resource);
336 return;
339 std::pair<PendingMasters::iterator, bool> ret =
340 pending_master_entries_.insert(
341 PendingMasters::value_type(new_master_resource, PendingHosts()));
342 is_new_pending_master_entry = ret.second;
343 ret.first->second.push_back(host);
344 host->AddObserver(this);
347 // Notify host (if any) if already checking or downloading.
348 AppCacheGroup::UpdateStatus update_status = group_->update_status();
349 if (update_status == AppCacheGroup::CHECKING ||
350 update_status == AppCacheGroup::DOWNLOADING) {
351 if (host) {
352 NotifySingleHost(host, CHECKING_EVENT);
353 if (update_status == AppCacheGroup::DOWNLOADING)
354 NotifySingleHost(host, DOWNLOADING_EVENT);
356 // Add to fetch list or an existing entry if already fetched.
357 if (!new_master_resource.is_empty()) {
358 AddMasterEntryToFetchList(host, new_master_resource,
359 is_new_pending_master_entry);
362 return;
365 // Begin update process for the group.
366 group_->SetUpdateStatus(AppCacheGroup::CHECKING);
367 if (group_->HasCache()) {
368 update_type_ = UPGRADE_ATTEMPT;
369 NotifyAllAssociatedHosts(CHECKING_EVENT);
370 } else {
371 update_type_ = CACHE_ATTEMPT;
372 DCHECK(host);
373 NotifySingleHost(host, CHECKING_EVENT);
376 if (!new_master_resource.is_empty()) {
377 AddMasterEntryToFetchList(host, new_master_resource,
378 is_new_pending_master_entry);
381 FetchManifest(true);
384 AppCacheResponseWriter* AppCacheUpdateJob::CreateResponseWriter() {
385 AppCacheResponseWriter* writer =
386 service_->storage()->CreateResponseWriter(manifest_url_,
387 group_->group_id());
388 stored_response_ids_.push_back(writer->response_id());
389 return writer;
392 void AppCacheUpdateJob::HandleCacheFailure(const std::string& error_message) {
393 // 6.9.4 cache failure steps 2-8.
394 DCHECK(internal_state_ != CACHE_FAILURE);
395 DCHECK(!error_message.empty());
396 internal_state_ = CACHE_FAILURE;
397 CancelAllUrlFetches();
398 CancelAllMasterEntryFetches(error_message);
399 NotifyAllError(error_message);
400 DiscardInprogressCache();
401 internal_state_ = COMPLETED;
402 DeleteSoon(); // To unwind the stack prior to deletion.
405 void AppCacheUpdateJob::FetchManifest(bool is_first_fetch) {
406 DCHECK(!manifest_fetcher_);
407 manifest_fetcher_ = new URLFetcher(
408 manifest_url_,
409 is_first_fetch ? URLFetcher::MANIFEST_FETCH :
410 URLFetcher::MANIFEST_REFETCH,
411 this);
413 // Add any necessary Http headers before sending fetch request.
414 if (is_first_fetch) {
415 AppCacheEntry* entry = (update_type_ == UPGRADE_ATTEMPT) ?
416 group_->newest_complete_cache()->GetEntry(manifest_url_) : NULL;
417 if (entry) {
418 // Asynchronously load response info for manifest from newest cache.
419 service_->storage()->LoadResponseInfo(manifest_url_, group_->group_id(),
420 entry->response_id(), this);
421 } else {
422 manifest_fetcher_->Start();
424 } else {
425 DCHECK(internal_state_ == REFETCH_MANIFEST);
426 DCHECK(manifest_response_info_.get());
427 manifest_fetcher_->set_existing_response_headers(
428 manifest_response_info_->headers.get());
429 manifest_fetcher_->Start();
434 void AppCacheUpdateJob::HandleManifestFetchCompleted(
435 URLFetcher* fetcher) {
436 DCHECK_EQ(internal_state_, FETCH_MANIFEST);
437 DCHECK_EQ(manifest_fetcher_, fetcher);
438 manifest_fetcher_ = NULL;
440 net::URLRequest* request = fetcher->request();
441 int response_code = -1;
442 bool is_valid_response_code = false;
443 if (request->status().is_success()) {
444 response_code = request->GetResponseCode();
445 is_valid_response_code = (response_code / 100 == 2);
448 if (is_valid_response_code) {
449 manifest_data_ = fetcher->manifest_data();
450 manifest_response_info_.reset(
451 new net::HttpResponseInfo(request->response_info()));
452 if (update_type_ == UPGRADE_ATTEMPT)
453 CheckIfManifestChanged(); // continues asynchronously
454 else
455 ContinueHandleManifestFetchCompleted(true);
456 } else if (response_code == 304 && update_type_ == UPGRADE_ATTEMPT) {
457 ContinueHandleManifestFetchCompleted(false);
458 } else if ((response_code == 404 || response_code == 410) &&
459 update_type_ == UPGRADE_ATTEMPT) {
460 service_->storage()->MakeGroupObsolete(group_, this); // async
461 } else {
462 const char* kFormatString = "Manifest fetch failed (%d) %s";
463 std::string message = base::StringPrintf(kFormatString, response_code,
464 manifest_url_.spec().c_str());
465 HandleCacheFailure(message);
469 void AppCacheUpdateJob::OnGroupMadeObsolete(AppCacheGroup* group,
470 bool success) {
471 DCHECK(master_entry_fetches_.empty());
472 CancelAllMasterEntryFetches("The cache has been made obsolete, "
473 "the manifest file returned 404 or 410");
474 if (success) {
475 DCHECK(group->is_obsolete());
476 NotifyAllAssociatedHosts(OBSOLETE_EVENT);
477 internal_state_ = COMPLETED;
478 MaybeCompleteUpdate();
479 } else {
480 // Treat failure to mark group obsolete as a cache failure.
481 HandleCacheFailure("Failed to mark the cache as obsolete");
485 void AppCacheUpdateJob::ContinueHandleManifestFetchCompleted(bool changed) {
486 DCHECK(internal_state_ == FETCH_MANIFEST);
488 if (!changed) {
489 DCHECK(update_type_ == UPGRADE_ATTEMPT);
490 internal_state_ = NO_UPDATE;
492 // Wait for pending master entries to download.
493 FetchMasterEntries();
494 MaybeCompleteUpdate(); // if not done, run async 6.9.4 step 7 substeps
495 return;
498 Manifest manifest;
499 if (!ParseManifest(manifest_url_, manifest_data_.data(),
500 manifest_data_.length(), manifest)) {
501 const char* kFormatString = "Failed to parse manifest %s";
502 const std::string message = base::StringPrintf(kFormatString,
503 manifest_url_.spec().c_str());
504 HandleCacheFailure(message);
505 VLOG(1) << message;
506 return;
509 // Proceed with update process. Section 6.9.4 steps 8-20.
510 internal_state_ = DOWNLOADING;
511 inprogress_cache_ = new AppCache(service_->storage(),
512 service_->storage()->NewCacheId());
513 BuildUrlFileList(manifest);
514 inprogress_cache_->InitializeWithManifest(&manifest);
516 // Associate all pending master hosts with the newly created cache.
517 for (PendingMasters::iterator it = pending_master_entries_.begin();
518 it != pending_master_entries_.end(); ++it) {
519 PendingHosts& hosts = it->second;
520 for (PendingHosts::iterator host_it = hosts.begin();
521 host_it != hosts.end(); ++host_it) {
522 (*host_it)
523 ->AssociateIncompleteCache(inprogress_cache_.get(), manifest_url_);
527 group_->SetUpdateStatus(AppCacheGroup::DOWNLOADING);
528 NotifyAllAssociatedHosts(DOWNLOADING_EVENT);
529 FetchUrls();
530 FetchMasterEntries();
531 MaybeCompleteUpdate(); // if not done, continues when async fetches complete
534 void AppCacheUpdateJob::HandleUrlFetchCompleted(URLFetcher* fetcher) {
535 DCHECK(internal_state_ == DOWNLOADING);
537 net::URLRequest* request = fetcher->request();
538 const GURL& url = request->original_url();
539 pending_url_fetches_.erase(url);
540 NotifyAllProgress(url);
541 ++url_fetches_completed_;
543 int response_code = request->status().is_success()
544 ? request->GetResponseCode() : -1;
545 AppCacheEntry& entry = url_file_list_.find(url)->second;
547 if (response_code / 100 == 2) {
548 // Associate storage with the new entry.
549 DCHECK(fetcher->response_writer());
550 entry.set_response_id(fetcher->response_writer()->response_id());
551 entry.set_response_size(fetcher->response_writer()->amount_written());
552 if (!inprogress_cache_->AddOrModifyEntry(url, entry))
553 duplicate_response_ids_.push_back(entry.response_id());
555 // TODO(michaeln): Check for <html manifest=xxx>
556 // See http://code.google.com/p/chromium/issues/detail?id=97930
557 // if (entry.IsMaster() && !(entry.IsExplicit() || fallback || intercept))
558 // if (!manifestAttribute) skip it
560 // Foreign entries will be detected during cache selection.
561 // Note: 6.9.4, step 17.9 possible optimization: if resource is HTML or XML
562 // file whose root element is an html element with a manifest attribute
563 // whose value doesn't match the manifest url of the application cache
564 // being processed, mark the entry as being foreign.
565 } else {
566 VLOG(1) << "Request status: " << request->status().status()
567 << " error: " << request->status().error()
568 << " response code: " << response_code;
569 if (entry.IsExplicit() || entry.IsFallback() || entry.IsIntercept()) {
570 if (response_code == 304 && fetcher->existing_entry().has_response_id()) {
571 // Keep the existing response.
572 entry.set_response_id(fetcher->existing_entry().response_id());
573 entry.set_response_size(fetcher->existing_entry().response_size());
574 inprogress_cache_->AddOrModifyEntry(url, entry);
575 } else {
576 const char* kFormatString = "Resource fetch failed (%d) %s";
577 const std::string message = base::StringPrintf(kFormatString,
578 response_code, url.spec().c_str());
579 HandleCacheFailure(message);
580 return;
582 } else if (response_code == 404 || response_code == 410) {
583 // Entry is skipped. They are dropped from the cache.
584 } else if (update_type_ == UPGRADE_ATTEMPT &&
585 fetcher->existing_entry().has_response_id()) {
586 // Keep the existing response.
587 // TODO(michaeln): Not sure this is a good idea. This is spec compliant
588 // but the old resource may or may not be compatible with the new contents
589 // of the cache. Impossible to know one way or the other.
590 entry.set_response_id(fetcher->existing_entry().response_id());
591 entry.set_response_size(fetcher->existing_entry().response_size());
592 inprogress_cache_->AddOrModifyEntry(url, entry);
596 // Fetch another URL now that one request has completed.
597 DCHECK(internal_state_ != CACHE_FAILURE);
598 FetchUrls();
599 MaybeCompleteUpdate();
602 void AppCacheUpdateJob::HandleMasterEntryFetchCompleted(
603 URLFetcher* fetcher) {
604 DCHECK(internal_state_ == NO_UPDATE || internal_state_ == DOWNLOADING);
606 // TODO(jennb): Handle downloads completing during cache failure when update
607 // no longer fetches master entries directly. For now, we cancel all pending
608 // master entry fetches when entering cache failure state so this will never
609 // be called in CACHE_FAILURE state.
611 net::URLRequest* request = fetcher->request();
612 const GURL& url = request->original_url();
613 master_entry_fetches_.erase(url);
614 ++master_entries_completed_;
616 int response_code = request->status().is_success()
617 ? request->GetResponseCode() : -1;
619 PendingMasters::iterator found = pending_master_entries_.find(url);
620 DCHECK(found != pending_master_entries_.end());
621 PendingHosts& hosts = found->second;
623 // Section 6.9.4. No update case: step 7.3, else step 22.
624 if (response_code / 100 == 2) {
625 // Add fetched master entry to the appropriate cache.
626 AppCache* cache = inprogress_cache_.get() ? inprogress_cache_.get()
627 : group_->newest_complete_cache();
628 DCHECK(fetcher->response_writer());
629 AppCacheEntry master_entry(AppCacheEntry::MASTER,
630 fetcher->response_writer()->response_id(),
631 fetcher->response_writer()->amount_written());
632 if (cache->AddOrModifyEntry(url, master_entry))
633 added_master_entries_.push_back(url);
634 else
635 duplicate_response_ids_.push_back(master_entry.response_id());
637 // In no-update case, associate host with the newest cache.
638 if (!inprogress_cache_.get()) {
639 // TODO(michaeln): defer until the updated cache has been stored
640 DCHECK(cache == group_->newest_complete_cache());
641 for (PendingHosts::iterator host_it = hosts.begin();
642 host_it != hosts.end(); ++host_it) {
643 (*host_it)->AssociateCompleteCache(cache);
646 } else {
647 HostNotifier host_notifier;
648 for (PendingHosts::iterator host_it = hosts.begin();
649 host_it != hosts.end(); ++host_it) {
650 AppCacheHost* host = *host_it;
651 host_notifier.AddHost(host);
653 // In downloading case, disassociate host from inprogress cache.
654 if (inprogress_cache_.get())
655 host->AssociateNoCache(GURL());
657 host->RemoveObserver(this);
659 hosts.clear();
661 const char* kFormatString = "Master entry fetch failed (%d) %s";
662 const std::string message = base::StringPrintf(kFormatString,
663 response_code, request->url().spec().c_str());
664 host_notifier.SendErrorNotifications(message);
666 // In downloading case, update result is different if all master entries
667 // failed vs. only some failing.
668 if (inprogress_cache_.get()) {
669 // Only count successful downloads to know if all master entries failed.
670 pending_master_entries_.erase(found);
671 --master_entries_completed_;
673 // Section 6.9.4, step 22.3.
674 if (update_type_ == CACHE_ATTEMPT && pending_master_entries_.empty()) {
675 HandleCacheFailure(message);
676 return;
681 DCHECK(internal_state_ != CACHE_FAILURE);
682 FetchMasterEntries();
683 MaybeCompleteUpdate();
686 void AppCacheUpdateJob::HandleManifestRefetchCompleted(
687 URLFetcher* fetcher) {
688 DCHECK(internal_state_ == REFETCH_MANIFEST);
689 DCHECK(manifest_fetcher_ == fetcher);
690 manifest_fetcher_ = NULL;
692 net::URLRequest* request = fetcher->request();
693 int response_code = request->status().is_success()
694 ? request->GetResponseCode() : -1;
695 if (response_code == 304 || manifest_data_ == fetcher->manifest_data()) {
696 // Only need to store response in storage if manifest is not already
697 // an entry in the cache.
698 AppCacheEntry* entry = inprogress_cache_->GetEntry(manifest_url_);
699 if (entry) {
700 entry->add_types(AppCacheEntry::MANIFEST);
701 StoreGroupAndCache();
702 } else {
703 manifest_response_writer_.reset(CreateResponseWriter());
704 scoped_refptr<HttpResponseInfoIOBuffer> io_buffer(
705 new HttpResponseInfoIOBuffer(manifest_response_info_.release()));
706 manifest_response_writer_->WriteInfo(
707 io_buffer.get(),
708 base::Bind(&AppCacheUpdateJob::OnManifestInfoWriteComplete,
709 base::Unretained(this)));
711 } else {
712 VLOG(1) << "Request status: " << request->status().status()
713 << " error: " << request->status().error()
714 << " response code: " << response_code;
715 ScheduleUpdateRetry(kRerunDelayMs);
716 HandleCacheFailure("Manifest changed during update, scheduling retry");
720 void AppCacheUpdateJob::OnManifestInfoWriteComplete(int result) {
721 if (result > 0) {
722 scoped_refptr<net::StringIOBuffer> io_buffer(
723 new net::StringIOBuffer(manifest_data_));
724 manifest_response_writer_->WriteData(
725 io_buffer.get(),
726 manifest_data_.length(),
727 base::Bind(&AppCacheUpdateJob::OnManifestDataWriteComplete,
728 base::Unretained(this)));
729 } else {
730 HandleCacheFailure("Failed to write the manifest headers to storage");
734 void AppCacheUpdateJob::OnManifestDataWriteComplete(int result) {
735 if (result > 0) {
736 AppCacheEntry entry(AppCacheEntry::MANIFEST,
737 manifest_response_writer_->response_id(),
738 manifest_response_writer_->amount_written());
739 if (!inprogress_cache_->AddOrModifyEntry(manifest_url_, entry))
740 duplicate_response_ids_.push_back(entry.response_id());
741 StoreGroupAndCache();
742 } else {
743 HandleCacheFailure("Failed to write the manifest data to storage");
747 void AppCacheUpdateJob::StoreGroupAndCache() {
748 DCHECK(stored_state_ == UNSTORED);
749 stored_state_ = STORING;
750 scoped_refptr<AppCache> newest_cache;
751 if (inprogress_cache_.get())
752 newest_cache.swap(inprogress_cache_);
753 else
754 newest_cache = group_->newest_complete_cache();
755 newest_cache->set_update_time(base::Time::Now());
757 // TODO(michaeln): dcheck is fishing for clues to crbug/95101
758 DCHECK_EQ(manifest_url_, group_->manifest_url());
759 service_->storage()
760 ->StoreGroupAndNewestCache(group_, newest_cache.get(), this); // async
763 void AppCacheUpdateJob::OnGroupAndNewestCacheStored(AppCacheGroup* group,
764 AppCache* newest_cache,
765 bool success,
766 bool would_exceed_quota) {
767 DCHECK(stored_state_ == STORING);
768 if (success) {
769 stored_state_ = STORED;
770 MaybeCompleteUpdate(); // will definitely complete
771 } else {
772 // Restore inprogress_cache_ to get the proper events delivered
773 // and the proper cleanup to occur.
774 if (newest_cache != group->newest_complete_cache())
775 inprogress_cache_ = newest_cache;
777 std::string message("Failed to commit new cache to storage");
778 if (would_exceed_quota)
779 message.append(", would exceed quota");
780 HandleCacheFailure(message);
784 void AppCacheUpdateJob::NotifySingleHost(AppCacheHost* host,
785 EventID event_id) {
786 std::vector<int> ids(1, host->host_id());
787 host->frontend()->OnEventRaised(ids, event_id);
790 void AppCacheUpdateJob::NotifyAllAssociatedHosts(EventID event_id) {
791 HostNotifier host_notifier;
792 AddAllAssociatedHostsToNotifier(&host_notifier);
793 host_notifier.SendNotifications(event_id);
796 void AppCacheUpdateJob::NotifyAllProgress(const GURL& url) {
797 HostNotifier host_notifier;
798 AddAllAssociatedHostsToNotifier(&host_notifier);
799 host_notifier.SendProgressNotifications(
800 url, url_file_list_.size(), url_fetches_completed_);
803 void AppCacheUpdateJob::NotifyAllFinalProgress() {
804 DCHECK(url_file_list_.size() == url_fetches_completed_);
805 NotifyAllProgress(GURL());
808 void AppCacheUpdateJob::NotifyAllError(const std::string& error_message) {
809 HostNotifier host_notifier;
810 AddAllAssociatedHostsToNotifier(&host_notifier);
811 host_notifier.SendErrorNotifications(error_message);
814 void AppCacheUpdateJob::AddAllAssociatedHostsToNotifier(
815 HostNotifier* host_notifier) {
816 // Collect hosts so we only send one notification per frontend.
817 // A host can only be associated with a single cache so no need to worry
818 // about duplicate hosts being added to the notifier.
819 if (inprogress_cache_.get()) {
820 DCHECK(internal_state_ == DOWNLOADING || internal_state_ == CACHE_FAILURE);
821 host_notifier->AddHosts(inprogress_cache_->associated_hosts());
824 AppCacheGroup::Caches old_caches = group_->old_caches();
825 for (AppCacheGroup::Caches::const_iterator it = old_caches.begin();
826 it != old_caches.end(); ++it) {
827 host_notifier->AddHosts((*it)->associated_hosts());
830 AppCache* newest_cache = group_->newest_complete_cache();
831 if (newest_cache)
832 host_notifier->AddHosts(newest_cache->associated_hosts());
835 void AppCacheUpdateJob::OnDestructionImminent(AppCacheHost* host) {
836 // The host is about to be deleted; remove from our collection.
837 PendingMasters::iterator found =
838 pending_master_entries_.find(host->pending_master_entry_url());
839 DCHECK(found != pending_master_entries_.end());
840 PendingHosts& hosts = found->second;
841 PendingHosts::iterator it = std::find(hosts.begin(), hosts.end(), host);
842 DCHECK(it != hosts.end());
843 hosts.erase(it);
846 void AppCacheUpdateJob::CheckIfManifestChanged() {
847 DCHECK(update_type_ == UPGRADE_ATTEMPT);
848 AppCacheEntry* entry =
849 group_->newest_complete_cache()->GetEntry(manifest_url_);
850 if (!entry) {
851 // TODO(michaeln): This is just a bandaid to avoid a crash.
852 // http://code.google.com/p/chromium/issues/detail?id=95101
853 HandleCacheFailure("Manifest entry not found in existing cache");
854 AppCacheHistograms::AddMissingManifestEntrySample();
855 service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
856 return;
859 // Load manifest data from storage to compare against fetched manifest.
860 manifest_response_reader_.reset(
861 service_->storage()->CreateResponseReader(manifest_url_,
862 group_->group_id(),
863 entry->response_id()));
864 read_manifest_buffer_ = new net::IOBuffer(kBufferSize);
865 manifest_response_reader_->ReadData(
866 read_manifest_buffer_.get(),
867 kBufferSize,
868 base::Bind(&AppCacheUpdateJob::OnManifestDataReadComplete,
869 base::Unretained(this))); // async read
872 void AppCacheUpdateJob::OnManifestDataReadComplete(int result) {
873 if (result > 0) {
874 loaded_manifest_data_.append(read_manifest_buffer_->data(), result);
875 manifest_response_reader_->ReadData(
876 read_manifest_buffer_.get(),
877 kBufferSize,
878 base::Bind(&AppCacheUpdateJob::OnManifestDataReadComplete,
879 base::Unretained(this))); // read more
880 } else {
881 read_manifest_buffer_ = NULL;
882 manifest_response_reader_.reset();
883 ContinueHandleManifestFetchCompleted(
884 result < 0 || manifest_data_ != loaded_manifest_data_);
888 void AppCacheUpdateJob::BuildUrlFileList(const Manifest& manifest) {
889 for (base::hash_set<std::string>::const_iterator it =
890 manifest.explicit_urls.begin();
891 it != manifest.explicit_urls.end(); ++it) {
892 AddUrlToFileList(GURL(*it), AppCacheEntry::EXPLICIT);
895 const std::vector<Namespace>& intercepts =
896 manifest.intercept_namespaces;
897 for (std::vector<Namespace>::const_iterator it = intercepts.begin();
898 it != intercepts.end(); ++it) {
899 int flags = AppCacheEntry::INTERCEPT;
900 if (it->is_executable)
901 flags |= AppCacheEntry::EXECUTABLE;
902 AddUrlToFileList(it->target_url, flags);
905 const std::vector<Namespace>& fallbacks =
906 manifest.fallback_namespaces;
907 for (std::vector<Namespace>::const_iterator it = fallbacks.begin();
908 it != fallbacks.end(); ++it) {
909 AddUrlToFileList(it->target_url, AppCacheEntry::FALLBACK);
912 // Add all master entries from newest complete cache.
913 if (update_type_ == UPGRADE_ATTEMPT) {
914 const AppCache::EntryMap& entries =
915 group_->newest_complete_cache()->entries();
916 for (AppCache::EntryMap::const_iterator it = entries.begin();
917 it != entries.end(); ++it) {
918 const AppCacheEntry& entry = it->second;
919 if (entry.IsMaster())
920 AddUrlToFileList(it->first, AppCacheEntry::MASTER);
925 void AppCacheUpdateJob::AddUrlToFileList(const GURL& url, int type) {
926 std::pair<AppCache::EntryMap::iterator, bool> ret = url_file_list_.insert(
927 AppCache::EntryMap::value_type(url, AppCacheEntry(type)));
929 if (ret.second)
930 urls_to_fetch_.push_back(UrlToFetch(url, false, NULL));
931 else
932 ret.first->second.add_types(type); // URL already exists. Merge types.
935 void AppCacheUpdateJob::FetchUrls() {
936 DCHECK(internal_state_ == DOWNLOADING);
938 // Fetch each URL in the list according to section 6.9.4 step 17.1-17.3.
939 // Fetch up to the concurrent limit. Other fetches will be triggered as each
940 // each fetch completes.
941 while (pending_url_fetches_.size() < kMaxConcurrentUrlFetches &&
942 !urls_to_fetch_.empty()) {
943 UrlToFetch url_to_fetch = urls_to_fetch_.front();
944 urls_to_fetch_.pop_front();
946 AppCache::EntryMap::iterator it = url_file_list_.find(url_to_fetch.url);
947 DCHECK(it != url_file_list_.end());
948 AppCacheEntry& entry = it->second;
949 if (ShouldSkipUrlFetch(entry)) {
950 NotifyAllProgress(url_to_fetch.url);
951 ++url_fetches_completed_;
952 } else if (AlreadyFetchedEntry(url_to_fetch.url, entry.types())) {
953 NotifyAllProgress(url_to_fetch.url);
954 ++url_fetches_completed_; // saved a URL request
955 } else if (!url_to_fetch.storage_checked &&
956 MaybeLoadFromNewestCache(url_to_fetch.url, entry)) {
957 // Continues asynchronously after data is loaded from newest cache.
958 } else {
959 URLFetcher* fetcher = new URLFetcher(
960 url_to_fetch.url, URLFetcher::URL_FETCH, this);
961 if (url_to_fetch.existing_response_info.get()) {
962 DCHECK(group_->newest_complete_cache());
963 AppCacheEntry* existing_entry =
964 group_->newest_complete_cache()->GetEntry(url_to_fetch.url);
965 DCHECK(existing_entry);
966 DCHECK(existing_entry->response_id() ==
967 url_to_fetch.existing_response_info->response_id());
968 fetcher->set_existing_response_headers(
969 url_to_fetch.existing_response_info->http_response_info()->headers
970 .get());
971 fetcher->set_existing_entry(*existing_entry);
973 fetcher->Start();
974 pending_url_fetches_.insert(
975 PendingUrlFetches::value_type(url_to_fetch.url, fetcher));
980 void AppCacheUpdateJob::CancelAllUrlFetches() {
981 // Cancel any pending URL requests.
982 for (PendingUrlFetches::iterator it = pending_url_fetches_.begin();
983 it != pending_url_fetches_.end(); ++it) {
984 delete it->second;
987 url_fetches_completed_ +=
988 pending_url_fetches_.size() + urls_to_fetch_.size();
989 pending_url_fetches_.clear();
990 urls_to_fetch_.clear();
993 bool AppCacheUpdateJob::ShouldSkipUrlFetch(const AppCacheEntry& entry) {
994 // 6.6.4 Step 17
995 // If the resource URL being processed was flagged as neither an
996 // "explicit entry" nor or a "fallback entry", then the user agent
997 // may skip this URL.
998 if (entry.IsExplicit() || entry.IsFallback() || entry.IsIntercept())
999 return false;
1001 // TODO(jennb): decide if entry should be skipped to expire it from cache
1002 return false;
1005 bool AppCacheUpdateJob::AlreadyFetchedEntry(const GURL& url,
1006 int entry_type) {
1007 DCHECK(internal_state_ == DOWNLOADING || internal_state_ == NO_UPDATE);
1008 AppCacheEntry* existing =
1009 inprogress_cache_.get() ? inprogress_cache_->GetEntry(url)
1010 : group_->newest_complete_cache()->GetEntry(url);
1011 if (existing) {
1012 existing->add_types(entry_type);
1013 return true;
1015 return false;
1018 void AppCacheUpdateJob::AddMasterEntryToFetchList(AppCacheHost* host,
1019 const GURL& url,
1020 bool is_new) {
1021 DCHECK(!IsTerminating());
1023 if (internal_state_ == DOWNLOADING || internal_state_ == NO_UPDATE) {
1024 AppCache* cache;
1025 if (inprogress_cache_.get()) {
1026 // always associate
1027 host->AssociateIncompleteCache(inprogress_cache_.get(), manifest_url_);
1028 cache = inprogress_cache_.get();
1029 } else {
1030 cache = group_->newest_complete_cache();
1033 // Update existing entry if it has already been fetched.
1034 AppCacheEntry* entry = cache->GetEntry(url);
1035 if (entry) {
1036 entry->add_types(AppCacheEntry::MASTER);
1037 if (internal_state_ == NO_UPDATE && !inprogress_cache_.get()) {
1038 // only associate if have entry
1039 host->AssociateCompleteCache(cache);
1041 if (is_new)
1042 ++master_entries_completed_; // pretend fetching completed
1043 return;
1047 // Add to fetch list if not already fetching.
1048 if (master_entry_fetches_.find(url) == master_entry_fetches_.end()) {
1049 master_entries_to_fetch_.insert(url);
1050 if (internal_state_ == DOWNLOADING || internal_state_ == NO_UPDATE)
1051 FetchMasterEntries();
1055 void AppCacheUpdateJob::FetchMasterEntries() {
1056 DCHECK(internal_state_ == NO_UPDATE || internal_state_ == DOWNLOADING);
1058 // Fetch each master entry in the list, up to the concurrent limit.
1059 // Additional fetches will be triggered as each fetch completes.
1060 while (master_entry_fetches_.size() < kMaxConcurrentUrlFetches &&
1061 !master_entries_to_fetch_.empty()) {
1062 const GURL& url = *master_entries_to_fetch_.begin();
1064 if (AlreadyFetchedEntry(url, AppCacheEntry::MASTER)) {
1065 ++master_entries_completed_; // saved a URL request
1067 // In no update case, associate hosts to newest cache in group
1068 // now that master entry has been "successfully downloaded".
1069 if (internal_state_ == NO_UPDATE) {
1070 // TODO(michaeln): defer until the updated cache has been stored.
1071 DCHECK(!inprogress_cache_.get());
1072 AppCache* cache = group_->newest_complete_cache();
1073 PendingMasters::iterator found = pending_master_entries_.find(url);
1074 DCHECK(found != pending_master_entries_.end());
1075 PendingHosts& hosts = found->second;
1076 for (PendingHosts::iterator host_it = hosts.begin();
1077 host_it != hosts.end(); ++host_it) {
1078 (*host_it)->AssociateCompleteCache(cache);
1081 } else {
1082 URLFetcher* fetcher = new URLFetcher(
1083 url, URLFetcher::MASTER_ENTRY_FETCH, this);
1084 fetcher->Start();
1085 master_entry_fetches_.insert(PendingUrlFetches::value_type(url, fetcher));
1088 master_entries_to_fetch_.erase(master_entries_to_fetch_.begin());
1092 void AppCacheUpdateJob::CancelAllMasterEntryFetches(
1093 const std::string& error_message) {
1094 // For now, cancel all in-progress fetches for master entries and pretend
1095 // all master entries fetches have completed.
1096 // TODO(jennb): Delete this when update no longer fetches master entries
1097 // directly.
1099 // Cancel all in-progress fetches.
1100 for (PendingUrlFetches::iterator it = master_entry_fetches_.begin();
1101 it != master_entry_fetches_.end(); ++it) {
1102 delete it->second;
1103 master_entries_to_fetch_.insert(it->first); // back in unfetched list
1105 master_entry_fetches_.clear();
1107 master_entries_completed_ += master_entries_to_fetch_.size();
1109 // Cache failure steps, step 2.
1110 // Pretend all master entries that have not yet been fetched have completed
1111 // downloading. Unassociate hosts from any appcache and send ERROR event.
1112 HostNotifier host_notifier;
1113 while (!master_entries_to_fetch_.empty()) {
1114 const GURL& url = *master_entries_to_fetch_.begin();
1115 PendingMasters::iterator found = pending_master_entries_.find(url);
1116 DCHECK(found != pending_master_entries_.end());
1117 PendingHosts& hosts = found->second;
1118 for (PendingHosts::iterator host_it = hosts.begin();
1119 host_it != hosts.end(); ++host_it) {
1120 AppCacheHost* host = *host_it;
1121 host->AssociateNoCache(GURL());
1122 host_notifier.AddHost(host);
1123 host->RemoveObserver(this);
1125 hosts.clear();
1127 master_entries_to_fetch_.erase(master_entries_to_fetch_.begin());
1129 host_notifier.SendErrorNotifications(error_message);
1132 bool AppCacheUpdateJob::MaybeLoadFromNewestCache(const GURL& url,
1133 AppCacheEntry& entry) {
1134 if (update_type_ != UPGRADE_ATTEMPT)
1135 return false;
1137 AppCache* newest = group_->newest_complete_cache();
1138 AppCacheEntry* copy_me = newest->GetEntry(url);
1139 if (!copy_me || !copy_me->has_response_id())
1140 return false;
1142 // Load HTTP headers for entry from newest cache.
1143 loading_responses_.insert(
1144 LoadingResponses::value_type(copy_me->response_id(), url));
1145 service_->storage()->LoadResponseInfo(manifest_url_, group_->group_id(),
1146 copy_me->response_id(),
1147 this);
1148 // Async: wait for OnResponseInfoLoaded to complete.
1149 return true;
1152 void AppCacheUpdateJob::OnResponseInfoLoaded(
1153 AppCacheResponseInfo* response_info, int64 response_id) {
1154 const net::HttpResponseInfo* http_info = response_info ?
1155 response_info->http_response_info() : NULL;
1157 // Needed response info for a manifest fetch request.
1158 if (internal_state_ == FETCH_MANIFEST) {
1159 if (http_info)
1160 manifest_fetcher_->set_existing_response_headers(
1161 http_info->headers.get());
1162 manifest_fetcher_->Start();
1163 return;
1166 LoadingResponses::iterator found = loading_responses_.find(response_id);
1167 DCHECK(found != loading_responses_.end());
1168 const GURL& url = found->second;
1170 if (!http_info) {
1171 LoadFromNewestCacheFailed(url, NULL); // no response found
1172 } else {
1173 // Check if response can be re-used according to HTTP caching semantics.
1174 // Responses with a "vary" header get treated as expired.
1175 const std::string name = "vary";
1176 std::string value;
1177 void* iter = NULL;
1178 if (!http_info->headers.get() ||
1179 http_info->headers->RequiresValidation(http_info->request_time,
1180 http_info->response_time,
1181 base::Time::Now()) ||
1182 http_info->headers->EnumerateHeader(&iter, name, &value)) {
1183 LoadFromNewestCacheFailed(url, response_info);
1184 } else {
1185 DCHECK(group_->newest_complete_cache());
1186 AppCacheEntry* copy_me = group_->newest_complete_cache()->GetEntry(url);
1187 DCHECK(copy_me);
1188 DCHECK(copy_me->response_id() == response_id);
1190 AppCache::EntryMap::iterator it = url_file_list_.find(url);
1191 DCHECK(it != url_file_list_.end());
1192 AppCacheEntry& entry = it->second;
1193 entry.set_response_id(response_id);
1194 entry.set_response_size(copy_me->response_size());
1195 inprogress_cache_->AddOrModifyEntry(url, entry);
1196 NotifyAllProgress(url);
1197 ++url_fetches_completed_;
1200 loading_responses_.erase(found);
1202 MaybeCompleteUpdate();
1205 void AppCacheUpdateJob::LoadFromNewestCacheFailed(
1206 const GURL& url, AppCacheResponseInfo* response_info) {
1207 if (internal_state_ == CACHE_FAILURE)
1208 return;
1210 // Re-insert url at front of fetch list. Indicate storage has been checked.
1211 urls_to_fetch_.push_front(UrlToFetch(url, true, response_info));
1212 FetchUrls();
1215 void AppCacheUpdateJob::MaybeCompleteUpdate() {
1216 DCHECK(internal_state_ != CACHE_FAILURE);
1218 // Must wait for any pending master entries or url fetches to complete.
1219 if (master_entries_completed_ != pending_master_entries_.size() ||
1220 url_fetches_completed_ != url_file_list_.size()) {
1221 DCHECK(internal_state_ != COMPLETED);
1222 return;
1225 switch (internal_state_) {
1226 case NO_UPDATE:
1227 if (master_entries_completed_ > 0) {
1228 switch (stored_state_) {
1229 case UNSTORED:
1230 StoreGroupAndCache();
1231 return;
1232 case STORING:
1233 return;
1234 case STORED:
1235 break;
1238 // 6.9.4 steps 7.3-7.7.
1239 NotifyAllAssociatedHosts(NO_UPDATE_EVENT);
1240 DiscardDuplicateResponses();
1241 internal_state_ = COMPLETED;
1242 break;
1243 case DOWNLOADING:
1244 internal_state_ = REFETCH_MANIFEST;
1245 FetchManifest(false);
1246 break;
1247 case REFETCH_MANIFEST:
1248 DCHECK(stored_state_ == STORED);
1249 NotifyAllFinalProgress();
1250 if (update_type_ == CACHE_ATTEMPT)
1251 NotifyAllAssociatedHosts(CACHED_EVENT);
1252 else
1253 NotifyAllAssociatedHosts(UPDATE_READY_EVENT);
1254 DiscardDuplicateResponses();
1255 internal_state_ = COMPLETED;
1256 break;
1257 case CACHE_FAILURE:
1258 NOTREACHED(); // See HandleCacheFailure
1259 break;
1260 default:
1261 break;
1264 // Let the stack unwind before deletion to make it less risky as this
1265 // method is called from multiple places in this file.
1266 if (internal_state_ == COMPLETED)
1267 DeleteSoon();
1270 void AppCacheUpdateJob::ScheduleUpdateRetry(int delay_ms) {
1271 // TODO(jennb): post a delayed task with the "same parameters" as this job
1272 // to retry the update at a later time. Need group, URLs of pending master
1273 // entries and their hosts.
1276 void AppCacheUpdateJob::Cancel() {
1277 internal_state_ = CANCELLED;
1279 if (manifest_fetcher_) {
1280 delete manifest_fetcher_;
1281 manifest_fetcher_ = NULL;
1284 for (PendingUrlFetches::iterator it = pending_url_fetches_.begin();
1285 it != pending_url_fetches_.end(); ++it) {
1286 delete it->second;
1288 pending_url_fetches_.clear();
1290 for (PendingUrlFetches::iterator it = master_entry_fetches_.begin();
1291 it != master_entry_fetches_.end(); ++it) {
1292 delete it->second;
1294 master_entry_fetches_.clear();
1296 ClearPendingMasterEntries();
1297 DiscardInprogressCache();
1299 // Delete response writer to avoid any callbacks.
1300 if (manifest_response_writer_)
1301 manifest_response_writer_.reset();
1303 service_->storage()->CancelDelegateCallbacks(this);
1306 void AppCacheUpdateJob::ClearPendingMasterEntries() {
1307 for (PendingMasters::iterator it = pending_master_entries_.begin();
1308 it != pending_master_entries_.end(); ++it) {
1309 PendingHosts& hosts = it->second;
1310 for (PendingHosts::iterator host_it = hosts.begin();
1311 host_it != hosts.end(); ++host_it) {
1312 (*host_it)->RemoveObserver(this);
1316 pending_master_entries_.clear();
1319 void AppCacheUpdateJob::DiscardInprogressCache() {
1320 service_->storage()->DoomResponses(manifest_url_, stored_response_ids_);
1322 if (!inprogress_cache_.get()) {
1323 // We have to undo the changes we made, if any, to the existing cache.
1324 for (std::vector<GURL>::iterator iter = added_master_entries_.begin();
1325 iter != added_master_entries_.end(); ++iter) {
1326 DCHECK(group_->newest_complete_cache());
1327 group_->newest_complete_cache()->RemoveEntry(*iter);
1329 return;
1332 AppCache::AppCacheHosts& hosts = inprogress_cache_->associated_hosts();
1333 while (!hosts.empty())
1334 (*hosts.begin())->AssociateNoCache(GURL());
1336 inprogress_cache_ = NULL;
1339 void AppCacheUpdateJob::DiscardDuplicateResponses() {
1340 service_->storage()->DoomResponses(manifest_url_, duplicate_response_ids_);
1343 void AppCacheUpdateJob::DeleteSoon() {
1344 ClearPendingMasterEntries();
1345 manifest_response_writer_.reset();
1346 service_->storage()->CancelDelegateCallbacks(this);
1348 // Break the connection with the group so the group cannot call delete
1349 // on this object after we've posted a task to delete ourselves.
1350 group_->SetUpdateStatus(AppCacheGroup::IDLE);
1351 group_ = NULL;
1353 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
1356 } // namespace appcache