OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "webkit/appcache/appcache_update_job.h" | 5 #include "webkit/appcache/appcache_update_job.h" |
6 | 6 |
7 #include "base/compiler_specific.h" | 7 #include "base/compiler_specific.h" |
8 #include "base/message_loop.h" | 8 #include "base/message_loop.h" |
9 #include "base/string_util.h" | 9 #include "base/string_util.h" |
10 #include "net/base/io_buffer.h" | 10 #include "net/base/io_buffer.h" |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
99 void SendProgressNotifications( | 99 void SendProgressNotifications( |
100 const GURL& url, int num_total, int num_complete) { | 100 const GURL& url, int num_total, int num_complete) { |
101 for (NotifyHostMap::iterator it = hosts_to_notify.begin(); | 101 for (NotifyHostMap::iterator it = hosts_to_notify.begin(); |
102 it != hosts_to_notify.end(); ++it) { | 102 it != hosts_to_notify.end(); ++it) { |
103 AppCacheFrontend* frontend = it->first; | 103 AppCacheFrontend* frontend = it->first; |
104 frontend->OnProgressEventRaised(it->second, url, | 104 frontend->OnProgressEventRaised(it->second, url, |
105 num_total, num_complete); | 105 num_total, num_complete); |
106 } | 106 } |
107 } | 107 } |
108 | 108 |
| 109 void SendErrorNotifications(const std::string& error_message) { |
| 110 DCHECK(!error_message.empty()); |
| 111 for (NotifyHostMap::iterator it = hosts_to_notify.begin(); |
| 112 it != hosts_to_notify.end(); ++it) { |
| 113 AppCacheFrontend* frontend = it->first; |
| 114 frontend->OnErrorEventRaised(it->second, error_message); |
| 115 } |
| 116 } |
| 117 |
109 private: | 118 private: |
110 NotifyHostMap hosts_to_notify; | 119 NotifyHostMap hosts_to_notify; |
111 }; | 120 }; |
112 | 121 |
113 AppCacheUpdateJob::AppCacheUpdateJob(AppCacheService* service, | 122 AppCacheUpdateJob::AppCacheUpdateJob(AppCacheService* service, |
114 AppCacheGroup* group) | 123 AppCacheGroup* group) |
115 : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), | 124 : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), |
116 service_(service), | 125 service_(service), |
117 group_(group), | 126 group_(group), |
118 update_type_(UNKNOWN_TYPE), | 127 update_type_(UNKNOWN_TYPE), |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
229 | 238 |
230 void AppCacheUpdateJob::OnPolicyCheckComplete(int rv) { | 239 void AppCacheUpdateJob::OnPolicyCheckComplete(int rv) { |
231 policy_callback_->Release(); // Balanced in CheckPolicy. | 240 policy_callback_->Release(); // Balanced in CheckPolicy. |
232 if (rv == net::OK) { | 241 if (rv == net::OK) { |
233 FetchManifest(true); | 242 FetchManifest(true); |
234 return; | 243 return; |
235 } | 244 } |
236 | 245 |
237 group_->NotifyContentBlocked(); | 246 group_->NotifyContentBlocked(); |
238 | 247 |
| 248 const char* kErrorMessage = |
| 249 "Cache creation was blocked by the content policy"; |
239 MessageLoop::current()->PostTask(FROM_HERE, | 250 MessageLoop::current()->PostTask(FROM_HERE, |
240 method_factory_.NewRunnableMethod( | 251 method_factory_.NewRunnableMethod( |
241 &AppCacheUpdateJob::HandleCacheFailure)); | 252 &AppCacheUpdateJob::HandleCacheFailure, |
| 253 kErrorMessage)); |
242 } | 254 } |
243 | 255 |
244 void AppCacheUpdateJob::HandleCacheFailure() { | 256 void AppCacheUpdateJob::HandleCacheFailure(const std::string& error_message) { |
245 // TODO(michaeln): For now this is only invoked from one point | 257 // 6.9.4 cache failure steps 2-8. |
246 // of failure. Overtime, attempt the migrate the various places | 258 DCHECK(internal_state_ != CACHE_FAILURE); |
247 // where we can detect a failure condition to use this same | 259 DCHECK(!error_message.empty()); |
248 // method to enter the cache_failure state. | |
249 internal_state_ = CACHE_FAILURE; | 260 internal_state_ = CACHE_FAILURE; |
250 CancelAllUrlFetches(); | 261 CancelAllUrlFetches(); |
251 CancelAllMasterEntryFetches(); | 262 CancelAllMasterEntryFetches(error_message); |
252 MaybeCompleteUpdate(); | 263 NotifyAllError(error_message); |
| 264 DiscardInprogressCache(); |
| 265 internal_state_ = COMPLETED; |
| 266 DeleteSoon(); // To unwind the stack prior to deletion. |
253 } | 267 } |
254 | 268 |
255 void AppCacheUpdateJob::FetchManifest(bool is_first_fetch) { | 269 void AppCacheUpdateJob::FetchManifest(bool is_first_fetch) { |
256 DCHECK(!manifest_url_request_); | 270 DCHECK(!manifest_url_request_); |
257 manifest_url_request_ = new URLRequest(manifest_url_, this); | 271 manifest_url_request_ = new URLRequest(manifest_url_, this); |
258 UpdateJobInfo::RequestType fetch_type = is_first_fetch ? | 272 UpdateJobInfo::RequestType fetch_type = is_first_fetch ? |
259 UpdateJobInfo::MANIFEST_FETCH : UpdateJobInfo::MANIFEST_REFETCH; | 273 UpdateJobInfo::MANIFEST_FETCH : UpdateJobInfo::MANIFEST_REFETCH; |
260 manifest_url_request_->SetUserData(this, new UpdateJobInfo(fetch_type)); | 274 manifest_url_request_->SetUserData(this, new UpdateJobInfo(fetch_type)); |
261 manifest_url_request_->set_context(service_->request_context()); | 275 manifest_url_request_->set_context(service_->request_context()); |
262 manifest_url_request_->set_load_flags( | 276 manifest_url_request_->set_load_flags( |
(...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
505 new net::HttpResponseInfo(request->response_info())); | 519 new net::HttpResponseInfo(request->response_info())); |
506 if (update_type_ == UPGRADE_ATTEMPT) | 520 if (update_type_ == UPGRADE_ATTEMPT) |
507 CheckIfManifestChanged(); // continues asynchronously | 521 CheckIfManifestChanged(); // continues asynchronously |
508 else | 522 else |
509 ContinueHandleManifestFetchCompleted(true); | 523 ContinueHandleManifestFetchCompleted(true); |
510 } else if (response_code == 304 && update_type_ == UPGRADE_ATTEMPT) { | 524 } else if (response_code == 304 && update_type_ == UPGRADE_ATTEMPT) { |
511 ContinueHandleManifestFetchCompleted(false); | 525 ContinueHandleManifestFetchCompleted(false); |
512 } else if (response_code == 404 || response_code == 410) { | 526 } else if (response_code == 404 || response_code == 410) { |
513 service_->storage()->MakeGroupObsolete(group_, this); // async | 527 service_->storage()->MakeGroupObsolete(group_, this); // async |
514 } else { | 528 } else { |
515 internal_state_ = CACHE_FAILURE; | 529 const char* kFormatString = "Manifest fetch failed (%d) %s"; |
516 CancelAllMasterEntryFetches(); | 530 const std::string message = StringPrintf(kFormatString, response_code, |
517 MaybeCompleteUpdate(); // if not done, run async cache failure steps | 531 manifest_url_.spec().c_str()); |
| 532 HandleCacheFailure(message); |
518 } | 533 } |
519 } | 534 } |
520 | 535 |
521 void AppCacheUpdateJob::OnGroupMadeObsolete(AppCacheGroup* group, | 536 void AppCacheUpdateJob::OnGroupMadeObsolete(AppCacheGroup* group, |
522 bool success) { | 537 bool success) { |
523 DCHECK(master_entry_fetches_.empty()); | 538 DCHECK(master_entry_fetches_.empty()); |
524 CancelAllMasterEntryFetches(); | 539 CancelAllMasterEntryFetches("The group has been made obsolete"); |
525 if (success) { | 540 if (success) { |
526 DCHECK(group->is_obsolete()); | 541 DCHECK(group->is_obsolete()); |
527 NotifyAllAssociatedHosts(OBSOLETE_EVENT); | 542 NotifyAllAssociatedHosts(OBSOLETE_EVENT); |
528 internal_state_ = COMPLETED; | 543 internal_state_ = COMPLETED; |
| 544 MaybeCompleteUpdate(); |
529 } else { | 545 } else { |
530 // Treat failure to mark group obsolete as a cache failure. | 546 // Treat failure to mark group obsolete as a cache failure. |
531 internal_state_ = CACHE_FAILURE; | 547 HandleCacheFailure("Failed to make the group as obsolete"); |
532 } | 548 } |
533 MaybeCompleteUpdate(); | |
534 } | 549 } |
535 | 550 |
536 void AppCacheUpdateJob::ContinueHandleManifestFetchCompleted(bool changed) { | 551 void AppCacheUpdateJob::ContinueHandleManifestFetchCompleted(bool changed) { |
537 DCHECK(internal_state_ == FETCH_MANIFEST); | 552 DCHECK(internal_state_ == FETCH_MANIFEST); |
538 | 553 |
539 if (!changed) { | 554 if (!changed) { |
540 DCHECK(update_type_ == UPGRADE_ATTEMPT); | 555 DCHECK(update_type_ == UPGRADE_ATTEMPT); |
541 internal_state_ = NO_UPDATE; | 556 internal_state_ = NO_UPDATE; |
542 | 557 |
543 // Wait for pending master entries to download. | 558 // Wait for pending master entries to download. |
544 FetchMasterEntries(); | 559 FetchMasterEntries(); |
545 MaybeCompleteUpdate(); // if not done, run async 6.9.4 step 7 substeps | 560 MaybeCompleteUpdate(); // if not done, run async 6.9.4 step 7 substeps |
546 return; | 561 return; |
547 } | 562 } |
548 | 563 |
549 Manifest manifest; | 564 Manifest manifest; |
550 if (!ParseManifest(manifest_url_, manifest_data_.data(), | 565 if (!ParseManifest(manifest_url_, manifest_data_.data(), |
551 manifest_data_.length(), manifest)) { | 566 manifest_data_.length(), manifest)) { |
552 LOG(INFO) << "Failed to parse manifest: " << manifest_url_; | 567 const char* kFormatString = "Failed to parse manifest %s"; |
553 internal_state_ = CACHE_FAILURE; | 568 const std::string message = StringPrintf(kFormatString, |
554 CancelAllMasterEntryFetches(); | 569 manifest_url_.spec().c_str()); |
555 MaybeCompleteUpdate(); // if not done, run async cache failure steps | 570 HandleCacheFailure(message); |
| 571 LOG(INFO) << message; |
556 return; | 572 return; |
557 } | 573 } |
558 | 574 |
559 // Proceed with update process. Section 6.9.4 steps 8-20. | 575 // Proceed with update process. Section 6.9.4 steps 8-20. |
560 internal_state_ = DOWNLOADING; | 576 internal_state_ = DOWNLOADING; |
561 inprogress_cache_ = new AppCache(service_, | 577 inprogress_cache_ = new AppCache(service_, |
562 service_->storage()->NewCacheId()); | 578 service_->storage()->NewCacheId()); |
563 BuildUrlFileList(manifest); | 579 BuildUrlFileList(manifest); |
564 inprogress_cache_->InitializeWithManifest(&manifest); | 580 inprogress_cache_->InitializeWithManifest(&manifest); |
565 | 581 |
(...skipping 12 matching lines...) Expand all Loading... |
578 FetchUrls(); | 594 FetchUrls(); |
579 FetchMasterEntries(); | 595 FetchMasterEntries(); |
580 MaybeCompleteUpdate(); // if not done, continues when async fetches complete | 596 MaybeCompleteUpdate(); // if not done, continues when async fetches complete |
581 } | 597 } |
582 | 598 |
583 void AppCacheUpdateJob::HandleUrlFetchCompleted(URLRequest* request) { | 599 void AppCacheUpdateJob::HandleUrlFetchCompleted(URLRequest* request) { |
584 DCHECK(internal_state_ == DOWNLOADING); | 600 DCHECK(internal_state_ == DOWNLOADING); |
585 | 601 |
586 const GURL& url = request->original_url(); | 602 const GURL& url = request->original_url(); |
587 pending_url_fetches_.erase(url); | 603 pending_url_fetches_.erase(url); |
588 NotifyProgress(url); | 604 NotifyAllProgress(url); |
589 ++url_fetches_completed_; | 605 ++url_fetches_completed_; |
590 | 606 |
591 int response_code = request->status().is_success() | 607 int response_code = request->status().is_success() |
592 ? request->GetResponseCode() : -1; | 608 ? request->GetResponseCode() : -1; |
593 AppCacheEntry& entry = url_file_list_.find(url)->second; | 609 AppCacheEntry& entry = url_file_list_.find(url)->second; |
594 | 610 |
595 if (request->status().is_success() && (response_code / 100 == 2)) { | 611 if (request->status().is_success() && (response_code / 100 == 2)) { |
596 // Associate storage with the new entry. | 612 // Associate storage with the new entry. |
597 UpdateJobInfo* info = | 613 UpdateJobInfo* info = |
598 static_cast<UpdateJobInfo*>(request->GetUserData(this)); | 614 static_cast<UpdateJobInfo*>(request->GetUserData(this)); |
599 DCHECK(info->response_writer_.get()); | 615 DCHECK(info->response_writer_.get()); |
600 entry.set_response_id(info->response_writer_->response_id()); | 616 entry.set_response_id(info->response_writer_->response_id()); |
601 entry.set_response_size(info->response_writer_->amount_written()); | 617 entry.set_response_size(info->response_writer_->amount_written()); |
602 | 618 |
603 if (!inprogress_cache_->AddOrModifyEntry(url, entry)) | 619 if (!inprogress_cache_->AddOrModifyEntry(url, entry)) |
604 duplicate_response_ids_.push_back(entry.response_id()); | 620 duplicate_response_ids_.push_back(entry.response_id()); |
605 | 621 |
606 // Foreign entries will be detected during cache selection. | 622 // Foreign entries will be detected during cache selection. |
607 // Note: 6.9.4, step 17.9 possible optimization: if resource is HTML or XML | 623 // Note: 6.9.4, step 17.9 possible optimization: if resource is HTML or XML |
608 // file whose root element is an html element with a manifest attribute | 624 // file whose root element is an html element with a manifest attribute |
609 // whose value doesn't match the manifest url of the application cache | 625 // whose value doesn't match the manifest url of the application cache |
610 // being processed, mark the entry as being foreign. | 626 // being processed, mark the entry as being foreign. |
611 } else { | 627 } else { |
612 LOG(INFO) << "Request status: " << request->status().status() | 628 LOG(INFO) << "Request status: " << request->status().status() |
613 << " os_error: " << request->status().os_error() | 629 << " os_error: " << request->status().os_error() |
614 << " response code: " << response_code; | 630 << " response code: " << response_code; |
615 if (entry.IsExplicit() || entry.IsFallback()) { | 631 if (entry.IsExplicit() || entry.IsFallback()) { |
616 internal_state_ = CACHE_FAILURE; | 632 const char* kFormatString = "Resource fetch failed (%d) %s"; |
617 CancelAllUrlFetches(); | 633 const std::string message = StringPrintf(kFormatString, response_code, |
618 CancelAllMasterEntryFetches(); | 634 request->url().spec().c_str()); |
| 635 HandleCacheFailure(message); |
| 636 return; |
619 } else if (response_code == 404 || response_code == 410) { | 637 } else if (response_code == 404 || response_code == 410) { |
620 // Entry is skipped. They are dropped from the cache. | 638 // Entry is skipped. They are dropped from the cache. |
621 } else if (update_type_ == UPGRADE_ATTEMPT) { | 639 } else if (update_type_ == UPGRADE_ATTEMPT) { |
622 // Copy the response from the newest complete cache. | 640 // Copy the response from the newest complete cache. |
623 AppCache* cache = group_->newest_complete_cache(); | 641 AppCache* cache = group_->newest_complete_cache(); |
624 AppCacheEntry* copy = cache->GetEntry(url); | 642 AppCacheEntry* copy = cache->GetEntry(url); |
625 if (copy) { | 643 if (copy) { |
626 entry.set_response_id(copy->response_id()); | 644 entry.set_response_id(copy->response_id()); |
627 entry.set_response_size(copy->response_size()); | 645 entry.set_response_size(copy->response_size()); |
628 inprogress_cache_->AddOrModifyEntry(url, entry); | 646 inprogress_cache_->AddOrModifyEntry(url, entry); |
629 } | 647 } |
630 } | 648 } |
631 } | 649 } |
632 | 650 |
633 // Fetch another URL now that one request has completed. | 651 // Fetch another URL now that one request has completed. |
634 if (internal_state_ != CACHE_FAILURE) | 652 DCHECK(internal_state_ != CACHE_FAILURE); |
635 FetchUrls(); | 653 FetchUrls(); |
636 | |
637 MaybeCompleteUpdate(); | 654 MaybeCompleteUpdate(); |
638 } | 655 } |
639 | 656 |
640 void AppCacheUpdateJob::HandleMasterEntryFetchCompleted(URLRequest* request) { | 657 void AppCacheUpdateJob::HandleMasterEntryFetchCompleted(URLRequest* request) { |
641 DCHECK(internal_state_ == NO_UPDATE || internal_state_ == DOWNLOADING); | 658 DCHECK(internal_state_ == NO_UPDATE || internal_state_ == DOWNLOADING); |
642 | 659 |
643 // TODO(jennb): Handle downloads completing during cache failure when update | 660 // TODO(jennb): Handle downloads completing during cache failure when update |
644 // no longer fetches master entries directly. For now, we cancel all pending | 661 // no longer fetches master entries directly. For now, we cancel all pending |
645 // master entry fetches when entering cache failure state so this will never | 662 // master entry fetches when entering cache failure state so this will never |
646 // be called in CACHE_FAILURE state. | 663 // be called in CACHE_FAILURE state. |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
686 AppCacheHost* host = *host_it; | 703 AppCacheHost* host = *host_it; |
687 host_notifier.AddHost(host); | 704 host_notifier.AddHost(host); |
688 | 705 |
689 // In downloading case, disassociate host from inprogress cache. | 706 // In downloading case, disassociate host from inprogress cache. |
690 if (inprogress_cache_) | 707 if (inprogress_cache_) |
691 host->AssociateCache(NULL); | 708 host->AssociateCache(NULL); |
692 | 709 |
693 host->RemoveObserver(this); | 710 host->RemoveObserver(this); |
694 } | 711 } |
695 hosts.clear(); | 712 hosts.clear(); |
696 host_notifier.SendNotifications(ERROR_EVENT); | 713 |
| 714 const char* kFormatString = "Master entry fetch failed (%d) %s"; |
| 715 const std::string message = StringPrintf(kFormatString, response_code, |
| 716 request->url().spec().c_str()); |
| 717 host_notifier.SendErrorNotifications(message); |
697 | 718 |
698 // In downloading case, update result is different if all master entries | 719 // In downloading case, update result is different if all master entries |
699 // failed vs. only some failing. | 720 // failed vs. only some failing. |
700 if (inprogress_cache_) { | 721 if (inprogress_cache_) { |
701 // Only count successful downloads to know if all master entries failed. | 722 // Only count successful downloads to know if all master entries failed. |
702 pending_master_entries_.erase(found); | 723 pending_master_entries_.erase(found); |
703 --master_entries_completed_; | 724 --master_entries_completed_; |
704 | 725 |
705 // Section 6.9.4, step 22.3. | 726 // Section 6.9.4, step 22.3. |
706 if (update_type_ == CACHE_ATTEMPT && pending_master_entries_.empty()) { | 727 if (update_type_ == CACHE_ATTEMPT && pending_master_entries_.empty()) { |
707 CancelAllUrlFetches(); | 728 HandleCacheFailure(message); |
708 internal_state_ = CACHE_FAILURE; | 729 return; |
709 } | 730 } |
710 } | 731 } |
711 } | 732 } |
712 | 733 |
713 if (internal_state_ != CACHE_FAILURE) | 734 DCHECK(internal_state_ != CACHE_FAILURE); |
714 FetchMasterEntries(); | 735 FetchMasterEntries(); |
715 | |
716 MaybeCompleteUpdate(); | 736 MaybeCompleteUpdate(); |
717 } | 737 } |
718 | 738 |
719 void AppCacheUpdateJob::HandleManifestRefetchCompleted(URLRequest* request) { | 739 void AppCacheUpdateJob::HandleManifestRefetchCompleted(URLRequest* request) { |
720 DCHECK(internal_state_ == REFETCH_MANIFEST); | 740 DCHECK(internal_state_ == REFETCH_MANIFEST); |
721 manifest_url_request_ = NULL; | 741 manifest_url_request_ = NULL; |
722 | 742 |
723 int response_code = request->status().is_success() | 743 int response_code = request->status().is_success() |
724 ? request->GetResponseCode() : -1; | 744 ? request->GetResponseCode() : -1; |
725 if (response_code == 304 || manifest_data_ == manifest_refetch_data_) { | 745 if (response_code == 304 || manifest_data_ == manifest_refetch_data_) { |
(...skipping 10 matching lines...) Expand all Loading... |
736 scoped_refptr<HttpResponseInfoIOBuffer> io_buffer = | 756 scoped_refptr<HttpResponseInfoIOBuffer> io_buffer = |
737 new HttpResponseInfoIOBuffer(manifest_response_info_.release()); | 757 new HttpResponseInfoIOBuffer(manifest_response_info_.release()); |
738 manifest_response_writer_->WriteInfo(io_buffer, | 758 manifest_response_writer_->WriteInfo(io_buffer, |
739 &manifest_info_write_callback_); | 759 &manifest_info_write_callback_); |
740 } | 760 } |
741 } else { | 761 } else { |
742 LOG(INFO) << "Request status: " << request->status().status() | 762 LOG(INFO) << "Request status: " << request->status().status() |
743 << " os_error: " << request->status().os_error() | 763 << " os_error: " << request->status().os_error() |
744 << " response code: " << response_code; | 764 << " response code: " << response_code; |
745 ScheduleUpdateRetry(kRerunDelayMs); | 765 ScheduleUpdateRetry(kRerunDelayMs); |
746 internal_state_ = CACHE_FAILURE; | 766 HandleCacheFailure("Manifest changed during update, scheduling retry"); |
747 MaybeCompleteUpdate(); // will definitely complete | |
748 } | 767 } |
749 } | 768 } |
750 | 769 |
751 void AppCacheUpdateJob::OnManifestInfoWriteComplete(int result) { | 770 void AppCacheUpdateJob::OnManifestInfoWriteComplete(int result) { |
752 if (result > 0) { | 771 if (result > 0) { |
753 scoped_refptr<net::StringIOBuffer> io_buffer = | 772 scoped_refptr<net::StringIOBuffer> io_buffer = |
754 new net::StringIOBuffer(manifest_data_); | 773 new net::StringIOBuffer(manifest_data_); |
755 manifest_response_writer_->WriteData(io_buffer, manifest_data_.length(), | 774 manifest_response_writer_->WriteData(io_buffer, manifest_data_.length(), |
756 &manifest_data_write_callback_); | 775 &manifest_data_write_callback_); |
757 } else { | 776 } else { |
758 internal_state_ = CACHE_FAILURE; | 777 HandleCacheFailure("Failed to write the manifest headers to storage"); |
759 MaybeCompleteUpdate(); // will definitely complete | |
760 } | 778 } |
761 } | 779 } |
762 | 780 |
763 void AppCacheUpdateJob::OnManifestDataWriteComplete(int result) { | 781 void AppCacheUpdateJob::OnManifestDataWriteComplete(int result) { |
764 if (result > 0) { | 782 if (result > 0) { |
765 AppCacheEntry entry(AppCacheEntry::MANIFEST, | 783 AppCacheEntry entry(AppCacheEntry::MANIFEST, |
766 manifest_response_writer_->response_id(), | 784 manifest_response_writer_->response_id(), |
767 manifest_response_writer_->amount_written()); | 785 manifest_response_writer_->amount_written()); |
768 if (!inprogress_cache_->AddOrModifyEntry(manifest_url_, entry)) | 786 if (!inprogress_cache_->AddOrModifyEntry(manifest_url_, entry)) |
769 duplicate_response_ids_.push_back(entry.response_id()); | 787 duplicate_response_ids_.push_back(entry.response_id()); |
770 StoreGroupAndCache(); | 788 StoreGroupAndCache(); |
771 } else { | 789 } else { |
772 internal_state_ = CACHE_FAILURE; | 790 HandleCacheFailure("Failed to write the manifest data to storage"); |
773 MaybeCompleteUpdate(); // will definitely complete | |
774 } | 791 } |
775 } | 792 } |
776 | 793 |
777 void AppCacheUpdateJob::StoreGroupAndCache() { | 794 void AppCacheUpdateJob::StoreGroupAndCache() { |
778 DCHECK(stored_state_ == UNSTORED); | 795 DCHECK(stored_state_ == UNSTORED); |
779 stored_state_ = STORING; | 796 stored_state_ = STORING; |
780 scoped_refptr<AppCache> newest_cache; | 797 scoped_refptr<AppCache> newest_cache; |
781 if (inprogress_cache_) | 798 if (inprogress_cache_) |
782 newest_cache.swap(inprogress_cache_); | 799 newest_cache.swap(inprogress_cache_); |
783 else | 800 else |
784 newest_cache = group_->newest_complete_cache(); | 801 newest_cache = group_->newest_complete_cache(); |
785 newest_cache->set_update_time(base::Time::Now()); | 802 newest_cache->set_update_time(base::Time::Now()); |
786 service_->storage()->StoreGroupAndNewestCache(group_, newest_cache, | 803 service_->storage()->StoreGroupAndNewestCache(group_, newest_cache, |
787 this); // async | 804 this); // async |
788 } | 805 } |
789 | 806 |
790 void AppCacheUpdateJob::OnGroupAndNewestCacheStored(AppCacheGroup* group, | 807 void AppCacheUpdateJob::OnGroupAndNewestCacheStored(AppCacheGroup* group, |
791 AppCache* newest_cache, | 808 AppCache* newest_cache, |
792 bool success) { | 809 bool success) { |
793 DCHECK(stored_state_ == STORING); | 810 DCHECK(stored_state_ == STORING); |
794 if (success) { | 811 if (success) { |
795 stored_state_ = STORED; | 812 stored_state_ = STORED; |
| 813 MaybeCompleteUpdate(); // will definitely complete |
796 } else { | 814 } else { |
797 internal_state_ = CACHE_FAILURE; | |
798 | |
799 // Restore inprogress_cache_ to get the proper events delivered | 815 // Restore inprogress_cache_ to get the proper events delivered |
800 // and the proper cleanup to occur. | 816 // and the proper cleanup to occur. |
801 if (newest_cache != group->newest_complete_cache()) | 817 if (newest_cache != group->newest_complete_cache()) |
802 inprogress_cache_ = newest_cache; | 818 inprogress_cache_ = newest_cache; |
| 819 |
| 820 HandleCacheFailure("Failed to commit new cache to storage"); |
803 } | 821 } |
804 MaybeCompleteUpdate(); // will definitely complete | |
805 } | 822 } |
806 | 823 |
807 void AppCacheUpdateJob::NotifySingleHost(AppCacheHost* host, | 824 void AppCacheUpdateJob::NotifySingleHost(AppCacheHost* host, |
808 EventID event_id) { | 825 EventID event_id) { |
809 std::vector<int> ids(1, host->host_id()); | 826 std::vector<int> ids(1, host->host_id()); |
810 host->frontend()->OnEventRaised(ids, event_id); | 827 host->frontend()->OnEventRaised(ids, event_id); |
811 } | 828 } |
812 | 829 |
813 void AppCacheUpdateJob::NotifyAllPendingMasterHosts(EventID event_id) { | |
814 // Collect hosts so we only send one notification per frontend. | |
815 // A host can only be associated with a single pending master entry | |
816 // so no need to worry about duplicate hosts being added to the notifier. | |
817 HostNotifier host_notifier; | |
818 for (PendingMasters::iterator it = pending_master_entries_.begin(); | |
819 it != pending_master_entries_.end(); ++it) { | |
820 PendingHosts& hosts = it->second; | |
821 for (PendingHosts::iterator host_it = hosts.begin(); | |
822 host_it != hosts.end(); ++host_it) { | |
823 host_notifier.AddHost(*host_it); | |
824 } | |
825 } | |
826 | |
827 host_notifier.SendNotifications(event_id); | |
828 } | |
829 | |
830 void AppCacheUpdateJob::NotifyAllAssociatedHosts(EventID event_id) { | 830 void AppCacheUpdateJob::NotifyAllAssociatedHosts(EventID event_id) { |
831 HostNotifier host_notifier; | 831 HostNotifier host_notifier; |
832 AddAllAssociatedHostsToNotifier(&host_notifier); | 832 AddAllAssociatedHostsToNotifier(&host_notifier); |
833 host_notifier.SendNotifications(event_id); | 833 host_notifier.SendNotifications(event_id); |
834 } | 834 } |
835 | 835 |
836 void AppCacheUpdateJob::NotifyProgress(const GURL& url) { | 836 void AppCacheUpdateJob::NotifyAllProgress(const GURL& url) { |
837 HostNotifier host_notifier; | 837 HostNotifier host_notifier; |
838 AddAllAssociatedHostsToNotifier(&host_notifier); | 838 AddAllAssociatedHostsToNotifier(&host_notifier); |
839 host_notifier.SendProgressNotifications( | 839 host_notifier.SendProgressNotifications( |
840 url, url_file_list_.size(), url_fetches_completed_); | 840 url, url_file_list_.size(), url_fetches_completed_); |
841 } | 841 } |
842 | 842 |
843 void AppCacheUpdateJob::NotifyFinalProgress() { | 843 void AppCacheUpdateJob::NotifyAllFinalProgress() { |
844 DCHECK(url_file_list_.size() == url_fetches_completed_); | 844 DCHECK(url_file_list_.size() == url_fetches_completed_); |
845 NotifyProgress(GURL()); | 845 NotifyAllProgress(GURL()); |
| 846 } |
| 847 |
| 848 void AppCacheUpdateJob::NotifyAllError(const std::string& error_message) { |
| 849 HostNotifier host_notifier; |
| 850 AddAllAssociatedHostsToNotifier(&host_notifier); |
| 851 host_notifier.SendErrorNotifications(error_message); |
846 } | 852 } |
847 | 853 |
848 void AppCacheUpdateJob::AddAllAssociatedHostsToNotifier( | 854 void AppCacheUpdateJob::AddAllAssociatedHostsToNotifier( |
849 HostNotifier* host_notifier) { | 855 HostNotifier* host_notifier) { |
850 // Collect hosts so we only send one notification per frontend. | 856 // Collect hosts so we only send one notification per frontend. |
851 // A host can only be associated with a single cache so no need to worry | 857 // A host can only be associated with a single cache so no need to worry |
852 // about duplicate hosts being added to the notifier. | 858 // about duplicate hosts being added to the notifier. |
853 if (inprogress_cache_) { | 859 if (inprogress_cache_) { |
854 DCHECK(internal_state_ == DOWNLOADING || internal_state_ == CACHE_FAILURE); | 860 DCHECK(internal_state_ == DOWNLOADING || internal_state_ == CACHE_FAILURE); |
855 host_notifier->AddHosts(inprogress_cache_->associated_hosts()); | 861 host_notifier->AddHosts(inprogress_cache_->associated_hosts()); |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
951 while (pending_url_fetches_.size() < kMaxConcurrentUrlFetches && | 957 while (pending_url_fetches_.size() < kMaxConcurrentUrlFetches && |
952 !urls_to_fetch_.empty()) { | 958 !urls_to_fetch_.empty()) { |
953 const GURL url = urls_to_fetch_.front().first; | 959 const GURL url = urls_to_fetch_.front().first; |
954 bool storage_checked = urls_to_fetch_.front().second; | 960 bool storage_checked = urls_to_fetch_.front().second; |
955 urls_to_fetch_.pop_front(); | 961 urls_to_fetch_.pop_front(); |
956 | 962 |
957 AppCache::EntryMap::iterator it = url_file_list_.find(url); | 963 AppCache::EntryMap::iterator it = url_file_list_.find(url); |
958 DCHECK(it != url_file_list_.end()); | 964 DCHECK(it != url_file_list_.end()); |
959 AppCacheEntry& entry = it->second; | 965 AppCacheEntry& entry = it->second; |
960 if (ShouldSkipUrlFetch(entry)) { | 966 if (ShouldSkipUrlFetch(entry)) { |
961 NotifyProgress(url); | 967 NotifyAllProgress(url); |
962 ++url_fetches_completed_; | 968 ++url_fetches_completed_; |
963 } else if (AlreadyFetchedEntry(url, entry.types())) { | 969 } else if (AlreadyFetchedEntry(url, entry.types())) { |
964 NotifyProgress(url); | 970 NotifyAllProgress(url); |
965 ++url_fetches_completed_; // saved a URL request | 971 ++url_fetches_completed_; // saved a URL request |
966 } else if (!storage_checked && MaybeLoadFromNewestCache(url, entry)) { | 972 } else if (!storage_checked && MaybeLoadFromNewestCache(url, entry)) { |
967 // Continues asynchronously after data is loaded from newest cache. | 973 // Continues asynchronously after data is loaded from newest cache. |
968 } else { | 974 } else { |
969 // Send URL request for the resource. | 975 // Send URL request for the resource. |
970 URLRequest* request = new URLRequest(url, this); | 976 URLRequest* request = new URLRequest(url, this); |
971 request->SetUserData(this, new UpdateJobInfo(UpdateJobInfo::URL_FETCH)); | 977 request->SetUserData(this, new UpdateJobInfo(UpdateJobInfo::URL_FETCH)); |
972 request->set_context(service_->request_context()); | 978 request->set_context(service_->request_context()); |
973 request->set_load_flags( | 979 request->set_load_flags( |
974 request->load_flags() | net::LOAD_DISABLE_INTERCEPT); | 980 request->load_flags() | net::LOAD_DISABLE_INTERCEPT); |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1082 request->set_load_flags( | 1088 request->set_load_flags( |
1083 request->load_flags() | net::LOAD_DISABLE_INTERCEPT); | 1089 request->load_flags() | net::LOAD_DISABLE_INTERCEPT); |
1084 request->Start(); | 1090 request->Start(); |
1085 master_entry_fetches_.insert(PendingUrlFetches::value_type(url, request)); | 1091 master_entry_fetches_.insert(PendingUrlFetches::value_type(url, request)); |
1086 } | 1092 } |
1087 | 1093 |
1088 master_entries_to_fetch_.erase(master_entries_to_fetch_.begin()); | 1094 master_entries_to_fetch_.erase(master_entries_to_fetch_.begin()); |
1089 } | 1095 } |
1090 } | 1096 } |
1091 | 1097 |
1092 void AppCacheUpdateJob::CancelAllMasterEntryFetches() { | 1098 void AppCacheUpdateJob::CancelAllMasterEntryFetches( |
| 1099 const std::string& error_message) { |
1093 // For now, cancel all in-progress fetches for master entries and pretend | 1100 // For now, cancel all in-progress fetches for master entries and pretend |
1094 // all master entries fetches have completed. | 1101 // all master entries fetches have completed. |
1095 // TODO(jennb): Delete this when update no longer fetches master entries | 1102 // TODO(jennb): Delete this when update no longer fetches master entries |
1096 // directly. | 1103 // directly. |
1097 | 1104 |
1098 // Cancel all in-progress fetches. | 1105 // Cancel all in-progress fetches. |
1099 for (PendingUrlFetches::iterator it = master_entry_fetches_.begin(); | 1106 for (PendingUrlFetches::iterator it = master_entry_fetches_.begin(); |
1100 it != master_entry_fetches_.end(); ++it) { | 1107 it != master_entry_fetches_.end(); ++it) { |
1101 delete it->second; | 1108 delete it->second; |
1102 master_entries_to_fetch_.insert(it->first); // back in unfetched list | 1109 master_entries_to_fetch_.insert(it->first); // back in unfetched list |
(...skipping 15 matching lines...) Expand all Loading... |
1118 host_it != hosts.end(); ++host_it) { | 1125 host_it != hosts.end(); ++host_it) { |
1119 AppCacheHost* host = *host_it; | 1126 AppCacheHost* host = *host_it; |
1120 host->AssociateCache(NULL); | 1127 host->AssociateCache(NULL); |
1121 host_notifier.AddHost(host); | 1128 host_notifier.AddHost(host); |
1122 host->RemoveObserver(this); | 1129 host->RemoveObserver(this); |
1123 } | 1130 } |
1124 hosts.clear(); | 1131 hosts.clear(); |
1125 | 1132 |
1126 master_entries_to_fetch_.erase(master_entries_to_fetch_.begin()); | 1133 master_entries_to_fetch_.erase(master_entries_to_fetch_.begin()); |
1127 } | 1134 } |
1128 host_notifier.SendNotifications(ERROR_EVENT); | 1135 host_notifier.SendErrorNotifications(error_message); |
1129 } | 1136 } |
1130 | 1137 |
1131 bool AppCacheUpdateJob::MaybeLoadFromNewestCache(const GURL& url, | 1138 bool AppCacheUpdateJob::MaybeLoadFromNewestCache(const GURL& url, |
1132 AppCacheEntry& entry) { | 1139 AppCacheEntry& entry) { |
1133 if (update_type_ != UPGRADE_ATTEMPT) | 1140 if (update_type_ != UPGRADE_ATTEMPT) |
1134 return false; | 1141 return false; |
1135 | 1142 |
1136 AppCache* newest = group_->newest_complete_cache(); | 1143 AppCache* newest = group_->newest_complete_cache(); |
1137 AppCacheEntry* copy_me = newest->GetEntry(url); | 1144 AppCacheEntry* copy_me = newest->GetEntry(url); |
1138 if (!copy_me || !copy_me->has_response_id()) | 1145 if (!copy_me || !copy_me->has_response_id()) |
(...skipping 28 matching lines...) Expand all Loading... |
1167 } else { | 1174 } else { |
1168 // Check if response can be re-used according to HTTP caching semantics. | 1175 // Check if response can be re-used according to HTTP caching semantics. |
1169 // Responses with a "vary" header get treated as expired. | 1176 // Responses with a "vary" header get treated as expired. |
1170 const std::string name = "vary"; | 1177 const std::string name = "vary"; |
1171 std::string value; | 1178 std::string value; |
1172 void* iter = NULL; | 1179 void* iter = NULL; |
1173 if (http_info->headers->RequiresValidation(http_info->request_time, | 1180 if (http_info->headers->RequiresValidation(http_info->request_time, |
1174 http_info->response_time, | 1181 http_info->response_time, |
1175 base::Time::Now()) || | 1182 base::Time::Now()) || |
1176 http_info->headers->EnumerateHeader(&iter, name, &value)) { | 1183 http_info->headers->EnumerateHeader(&iter, name, &value)) { |
| 1184 // TODO(michaeln): Make a conditional request when we can in this case. |
1177 LoadFromNewestCacheFailed(url); | 1185 LoadFromNewestCacheFailed(url); |
1178 } else { | 1186 } else { |
1179 DCHECK(group_->newest_complete_cache()); | 1187 DCHECK(group_->newest_complete_cache()); |
1180 AppCacheEntry* copy_me = group_->newest_complete_cache()->GetEntry(url); | 1188 AppCacheEntry* copy_me = group_->newest_complete_cache()->GetEntry(url); |
1181 DCHECK(copy_me); | 1189 DCHECK(copy_me); |
1182 DCHECK(copy_me->response_id() == response_id); | 1190 DCHECK(copy_me->response_id() == response_id); |
1183 | 1191 |
1184 AppCache::EntryMap::iterator it = url_file_list_.find(url); | 1192 AppCache::EntryMap::iterator it = url_file_list_.find(url); |
1185 DCHECK(it != url_file_list_.end()); | 1193 DCHECK(it != url_file_list_.end()); |
1186 AppCacheEntry& entry = it->second; | 1194 AppCacheEntry& entry = it->second; |
1187 entry.set_response_id(response_id); | 1195 entry.set_response_id(response_id); |
1188 entry.set_response_size(copy_me->response_size()); | 1196 entry.set_response_size(copy_me->response_size()); |
1189 inprogress_cache_->AddOrModifyEntry(url, entry); | 1197 inprogress_cache_->AddOrModifyEntry(url, entry); |
1190 NotifyProgress(url); | 1198 NotifyAllProgress(url); |
1191 ++url_fetches_completed_; | 1199 ++url_fetches_completed_; |
1192 } | 1200 } |
1193 } | 1201 } |
1194 loading_responses_.erase(found); | 1202 loading_responses_.erase(found); |
1195 | 1203 |
1196 MaybeCompleteUpdate(); | 1204 MaybeCompleteUpdate(); |
1197 } | 1205 } |
1198 | 1206 |
1199 void AppCacheUpdateJob::LoadFromNewestCacheFailed(const GURL& url) { | 1207 void AppCacheUpdateJob::LoadFromNewestCacheFailed(const GURL& url) { |
1200 if (internal_state_ == CACHE_FAILURE) | 1208 if (internal_state_ == CACHE_FAILURE) |
1201 return; | 1209 return; |
1202 | 1210 |
1203 // Re-insert url at front of fetch list. Indicate storage has been checked. | 1211 // Re-insert url at front of fetch list. Indicate storage has been checked. |
1204 urls_to_fetch_.push_front(AppCacheUpdateJob::UrlsToFetch(url, true)); | 1212 urls_to_fetch_.push_front(AppCacheUpdateJob::UrlsToFetch(url, true)); |
1205 FetchUrls(); | 1213 FetchUrls(); |
1206 } | 1214 } |
1207 | 1215 |
1208 void AppCacheUpdateJob::MaybeCompleteUpdate() { | 1216 void AppCacheUpdateJob::MaybeCompleteUpdate() { |
| 1217 DCHECK(internal_state_ != CACHE_FAILURE); |
| 1218 |
1209 // Must wait for any pending master entries or url fetches to complete. | 1219 // Must wait for any pending master entries or url fetches to complete. |
1210 if (master_entries_completed_ != pending_master_entries_.size() || | 1220 if (master_entries_completed_ != pending_master_entries_.size() || |
1211 url_fetches_completed_ != url_file_list_.size()) { | 1221 url_fetches_completed_ != url_file_list_.size()) { |
1212 DCHECK(internal_state_ != COMPLETED); | 1222 DCHECK(internal_state_ != COMPLETED); |
1213 return; | 1223 return; |
1214 } | 1224 } |
1215 | 1225 |
1216 switch (internal_state_) { | 1226 switch (internal_state_) { |
1217 case NO_UPDATE: | 1227 case NO_UPDATE: |
1218 if (master_entries_completed_ > 0) { | 1228 if (master_entries_completed_ > 0) { |
(...skipping 11 matching lines...) Expand all Loading... |
1230 NotifyAllAssociatedHosts(NO_UPDATE_EVENT); | 1240 NotifyAllAssociatedHosts(NO_UPDATE_EVENT); |
1231 DiscardDuplicateResponses(); | 1241 DiscardDuplicateResponses(); |
1232 internal_state_ = COMPLETED; | 1242 internal_state_ = COMPLETED; |
1233 break; | 1243 break; |
1234 case DOWNLOADING: | 1244 case DOWNLOADING: |
1235 internal_state_ = REFETCH_MANIFEST; | 1245 internal_state_ = REFETCH_MANIFEST; |
1236 FetchManifest(false); | 1246 FetchManifest(false); |
1237 break; | 1247 break; |
1238 case REFETCH_MANIFEST: | 1248 case REFETCH_MANIFEST: |
1239 DCHECK(stored_state_ == STORED); | 1249 DCHECK(stored_state_ == STORED); |
1240 NotifyFinalProgress(); | 1250 NotifyAllFinalProgress(); |
1241 if (update_type_ == CACHE_ATTEMPT) | 1251 if (update_type_ == CACHE_ATTEMPT) |
1242 NotifyAllAssociatedHosts(CACHED_EVENT); | 1252 NotifyAllAssociatedHosts(CACHED_EVENT); |
1243 else | 1253 else |
1244 NotifyAllAssociatedHosts(UPDATE_READY_EVENT); | 1254 NotifyAllAssociatedHosts(UPDATE_READY_EVENT); |
1245 DiscardDuplicateResponses(); | 1255 DiscardDuplicateResponses(); |
1246 internal_state_ = COMPLETED; | 1256 internal_state_ = COMPLETED; |
1247 break; | 1257 break; |
1248 case CACHE_FAILURE: | 1258 case CACHE_FAILURE: |
1249 // 6.9.4 cache failure steps 2-8. | 1259 NOTREACHED(); // See HandleCacheFailure |
1250 NotifyAllAssociatedHosts(ERROR_EVENT); | |
1251 DiscardInprogressCache(); | |
1252 // For a CACHE_ATTEMPT, group will be discarded when the host(s) that | |
1253 // started this update removes its reference to the group. Nothing more | |
1254 // to do here. | |
1255 internal_state_ = COMPLETED; | |
1256 break; | 1260 break; |
1257 default: | 1261 default: |
1258 break; | 1262 break; |
1259 } | 1263 } |
1260 | 1264 |
1261 // Let the stack unwind before deletion to make it less risky as this | 1265 // Let the stack unwind before deletion to make it less risky as this |
1262 // method is called from multiple places in this file. | 1266 // method is called from multiple places in this file. |
1263 if (internal_state_ == COMPLETED) | 1267 if (internal_state_ == COMPLETED) |
1264 DeleteSoon(); | 1268 DeleteSoon(); |
1265 } | 1269 } |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1339 | 1343 |
1340 // Break the connection with the group so the group cannot call delete | 1344 // Break the connection with the group so the group cannot call delete |
1341 // on this object after we've posted a task to delete ourselves. | 1345 // on this object after we've posted a task to delete ourselves. |
1342 group_->SetUpdateStatus(AppCacheGroup::IDLE); | 1346 group_->SetUpdateStatus(AppCacheGroup::IDLE); |
1343 group_ = NULL; | 1347 group_ = NULL; |
1344 | 1348 |
1345 MessageLoop::current()->DeleteSoon(FROM_HERE, this); | 1349 MessageLoop::current()->DeleteSoon(FROM_HERE, this); |
1346 } | 1350 } |
1347 | 1351 |
1348 } // namespace appcache | 1352 } // namespace appcache |
OLD | NEW |