| Index: webkit/browser/appcache/appcache_update_job.cc
|
| diff --git a/webkit/browser/appcache/appcache_update_job.cc b/webkit/browser/appcache/appcache_update_job.cc
|
| deleted file mode 100644
|
| index 0c5593c1deb1ce2e7e78c39a83772217a07fd856..0000000000000000000000000000000000000000
|
| --- a/webkit/browser/appcache/appcache_update_job.cc
|
| +++ /dev/null
|
| @@ -1,1611 +0,0 @@
|
| -// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "webkit/browser/appcache/appcache_update_job.h"
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/bind_helpers.h"
|
| -#include "base/compiler_specific.h"
|
| -#include "base/message_loop/message_loop.h"
|
| -#include "base/strings/string_util.h"
|
| -#include "base/strings/stringprintf.h"
|
| -#include "net/base/io_buffer.h"
|
| -#include "net/base/load_flags.h"
|
| -#include "net/base/net_errors.h"
|
| -#include "net/base/request_priority.h"
|
| -#include "net/http/http_request_headers.h"
|
| -#include "net/http/http_response_headers.h"
|
| -#include "net/url_request/url_request_context.h"
|
| -#include "webkit/browser/appcache/appcache_group.h"
|
| -#include "webkit/browser/appcache/appcache_histograms.h"
|
| -
|
| -namespace appcache {
|
| -
|
| -static const int kBufferSize = 32768;
|
| -static const size_t kMaxConcurrentUrlFetches = 2;
|
| -static const int kMax503Retries = 3;
|
| -
|
| -static std::string FormatUrlErrorMessage(
|
| - const char* format, const GURL& url,
|
| - AppCacheUpdateJob::ResultType error,
|
| - int response_code) {
|
| - // Show the net response code if we have one.
|
| - int code = response_code;
|
| - if (error != AppCacheUpdateJob::SERVER_ERROR)
|
| - code = static_cast<int>(error);
|
| - return base::StringPrintf(format, code, url.spec().c_str());
|
| -}
|
| -
|
| -// Helper class for collecting hosts per frontend when sending notifications
|
| -// so that only one notification is sent for all hosts using the same frontend.
|
| -class HostNotifier {
|
| - public:
|
| - typedef std::vector<int> HostIds;
|
| - typedef std::map<AppCacheFrontend*, HostIds> NotifyHostMap;
|
| -
|
| - // Caller is responsible for ensuring there will be no duplicate hosts.
|
| - void AddHost(AppCacheHost* host) {
|
| - std::pair<NotifyHostMap::iterator , bool> ret = hosts_to_notify.insert(
|
| - NotifyHostMap::value_type(host->frontend(), HostIds()));
|
| - ret.first->second.push_back(host->host_id());
|
| - }
|
| -
|
| - void AddHosts(const std::set<AppCacheHost*>& hosts) {
|
| - for (std::set<AppCacheHost*>::const_iterator it = hosts.begin();
|
| - it != hosts.end(); ++it) {
|
| - AddHost(*it);
|
| - }
|
| - }
|
| -
|
| - void SendNotifications(AppCacheEventID event_id) {
|
| - for (NotifyHostMap::iterator it = hosts_to_notify.begin();
|
| - it != hosts_to_notify.end(); ++it) {
|
| - AppCacheFrontend* frontend = it->first;
|
| - frontend->OnEventRaised(it->second, event_id);
|
| - }
|
| - }
|
| -
|
| - void SendProgressNotifications(
|
| - const GURL& url, int num_total, int num_complete) {
|
| - for (NotifyHostMap::iterator it = hosts_to_notify.begin();
|
| - it != hosts_to_notify.end(); ++it) {
|
| - AppCacheFrontend* frontend = it->first;
|
| - frontend->OnProgressEventRaised(it->second, url,
|
| - num_total, num_complete);
|
| - }
|
| - }
|
| -
|
| - void SendErrorNotifications(const AppCacheErrorDetails& details) {
|
| - DCHECK(!details.message.empty());
|
| - for (NotifyHostMap::iterator it = hosts_to_notify.begin();
|
| - it != hosts_to_notify.end(); ++it) {
|
| - AppCacheFrontend* frontend = it->first;
|
| - frontend->OnErrorEventRaised(it->second, details);
|
| - }
|
| - }
|
| -
|
| - void SendLogMessage(const std::string& message) {
|
| - for (NotifyHostMap::iterator it = hosts_to_notify.begin();
|
| - it != hosts_to_notify.end(); ++it) {
|
| - AppCacheFrontend* frontend = it->first;
|
| - for (HostIds::iterator id = it->second.begin();
|
| - id != it->second.end(); ++id) {
|
| - frontend->OnLogMessage(*id, APPCACHE_LOG_WARNING, message);
|
| - }
|
| - }
|
| - }
|
| -
|
| - private:
|
| - NotifyHostMap hosts_to_notify;
|
| -};
|
| -
|
| -AppCacheUpdateJob::UrlToFetch::UrlToFetch(const GURL& url,
|
| - bool checked,
|
| - AppCacheResponseInfo* info)
|
| - : url(url),
|
| - storage_checked(checked),
|
| - existing_response_info(info) {
|
| -}
|
| -
|
| -AppCacheUpdateJob::UrlToFetch::~UrlToFetch() {
|
| -}
|
| -
|
| -// Helper class to fetch resources. Depending on the fetch type,
|
| -// can either fetch to an in-memory string or write the response
|
| -// data out to the disk cache.
|
| -AppCacheUpdateJob::URLFetcher::URLFetcher(const GURL& url,
|
| - FetchType fetch_type,
|
| - AppCacheUpdateJob* job)
|
| - : url_(url),
|
| - job_(job),
|
| - fetch_type_(fetch_type),
|
| - retry_503_attempts_(0),
|
| - buffer_(new net::IOBuffer(kBufferSize)),
|
| - request_(job->service_->request_context()
|
| - ->CreateRequest(url, net::DEFAULT_PRIORITY, this, NULL)),
|
| - result_(UPDATE_OK),
|
| - redirect_response_code_(-1) {}
|
| -
|
| -AppCacheUpdateJob::URLFetcher::~URLFetcher() {
|
| -}
|
| -
|
| -void AppCacheUpdateJob::URLFetcher::Start() {
|
| - request_->set_first_party_for_cookies(job_->manifest_url_);
|
| - request_->SetLoadFlags(request_->load_flags() |
|
| - net::LOAD_DISABLE_INTERCEPT);
|
| - if (existing_response_headers_.get())
|
| - AddConditionalHeaders(existing_response_headers_.get());
|
| - request_->Start();
|
| -}
|
| -
|
| -void AppCacheUpdateJob::URLFetcher::OnReceivedRedirect(
|
| - net::URLRequest* request, const GURL& new_url, bool* defer_redirect) {
|
| - DCHECK(request_ == request);
|
| - // Redirect is not allowed by the update process.
|
| - job_->MadeProgress();
|
| - redirect_response_code_ = request->GetResponseCode();
|
| - request->Cancel();
|
| - result_ = REDIRECT_ERROR;
|
| - OnResponseCompleted();
|
| -}
|
| -
|
| -void AppCacheUpdateJob::URLFetcher::OnResponseStarted(
|
| - net::URLRequest *request) {
|
| - DCHECK(request == request_);
|
| - int response_code = -1;
|
| - if (request->status().is_success()) {
|
| - response_code = request->GetResponseCode();
|
| - job_->MadeProgress();
|
| - }
|
| - if ((response_code / 100) == 2) {
|
| -
|
| - // See http://code.google.com/p/chromium/issues/detail?id=69594
|
| - // We willfully violate the HTML5 spec at this point in order
|
| - // to support the appcaching of cross-origin HTTPS resources.
|
| - // We've opted for a milder constraint and allow caching unless
|
| - // the resource has a "no-store" header. A spec change has been
|
| - // requested on the whatwg list.
|
| - // TODO(michaeln): Consider doing this for cross-origin HTTP resources too.
|
| - if (url_.SchemeIsSecure() &&
|
| - url_.GetOrigin() != job_->manifest_url_.GetOrigin()) {
|
| - if (request->response_headers()->
|
| - HasHeaderValue("cache-control", "no-store")) {
|
| - DCHECK_EQ(-1, redirect_response_code_);
|
| - request->Cancel();
|
| - result_ = SERVER_ERROR; // Not the best match?
|
| - OnResponseCompleted();
|
| - return;
|
| - }
|
| - }
|
| -
|
| - // Write response info to storage for URL fetches. Wait for async write
|
| - // completion before reading any response data.
|
| - if (fetch_type_ == URL_FETCH || fetch_type_ == MASTER_ENTRY_FETCH) {
|
| - response_writer_.reset(job_->CreateResponseWriter());
|
| - scoped_refptr<HttpResponseInfoIOBuffer> io_buffer(
|
| - new HttpResponseInfoIOBuffer(
|
| - new net::HttpResponseInfo(request->response_info())));
|
| - response_writer_->WriteInfo(
|
| - io_buffer.get(),
|
| - base::Bind(&URLFetcher::OnWriteComplete, base::Unretained(this)));
|
| - } else {
|
| - ReadResponseData();
|
| - }
|
| - } else {
|
| - if (response_code > 0)
|
| - result_ = SERVER_ERROR;
|
| - else
|
| - result_ = NETWORK_ERROR;
|
| - OnResponseCompleted();
|
| - }
|
| -}
|
| -
|
| -void AppCacheUpdateJob::URLFetcher::OnReadCompleted(
|
| - net::URLRequest* request, int bytes_read) {
|
| - DCHECK(request_ == request);
|
| - bool data_consumed = true;
|
| - if (request->status().is_success() && bytes_read > 0) {
|
| - job_->MadeProgress();
|
| - data_consumed = ConsumeResponseData(bytes_read);
|
| - if (data_consumed) {
|
| - bytes_read = 0;
|
| - while (request->Read(buffer_.get(), kBufferSize, &bytes_read)) {
|
| - if (bytes_read > 0) {
|
| - data_consumed = ConsumeResponseData(bytes_read);
|
| - if (!data_consumed)
|
| - break; // wait for async data processing, then read more
|
| - } else {
|
| - break;
|
| - }
|
| - }
|
| - }
|
| - }
|
| - if (data_consumed && !request->status().is_io_pending()) {
|
| - DCHECK_EQ(UPDATE_OK, result_);
|
| - OnResponseCompleted();
|
| - }
|
| -}
|
| -
|
| -void AppCacheUpdateJob::URLFetcher::AddConditionalHeaders(
|
| - const net::HttpResponseHeaders* headers) {
|
| - DCHECK(request_.get() && headers);
|
| - net::HttpRequestHeaders extra_headers;
|
| -
|
| - // Add If-Modified-Since header if response info has Last-Modified header.
|
| - const std::string last_modified = "Last-Modified";
|
| - std::string last_modified_value;
|
| - headers->EnumerateHeader(NULL, last_modified, &last_modified_value);
|
| - if (!last_modified_value.empty()) {
|
| - extra_headers.SetHeader(net::HttpRequestHeaders::kIfModifiedSince,
|
| - last_modified_value);
|
| - }
|
| -
|
| - // Add If-None-Match header if response info has ETag header.
|
| - const std::string etag = "ETag";
|
| - std::string etag_value;
|
| - headers->EnumerateHeader(NULL, etag, &etag_value);
|
| - if (!etag_value.empty()) {
|
| - extra_headers.SetHeader(net::HttpRequestHeaders::kIfNoneMatch,
|
| - etag_value);
|
| - }
|
| - if (!extra_headers.IsEmpty())
|
| - request_->SetExtraRequestHeaders(extra_headers);
|
| -}
|
| -
|
| -void AppCacheUpdateJob::URLFetcher::OnWriteComplete(int result) {
|
| - if (result < 0) {
|
| - request_->Cancel();
|
| - result_ = DISKCACHE_ERROR;
|
| - OnResponseCompleted();
|
| - return;
|
| - }
|
| - ReadResponseData();
|
| -}
|
| -
|
| -void AppCacheUpdateJob::URLFetcher::ReadResponseData() {
|
| - InternalUpdateState state = job_->internal_state_;
|
| - if (state == CACHE_FAILURE || state == CANCELLED || state == COMPLETED)
|
| - return;
|
| - int bytes_read = 0;
|
| - request_->Read(buffer_.get(), kBufferSize, &bytes_read);
|
| - OnReadCompleted(request_.get(), bytes_read);
|
| -}
|
| -
|
| -// Returns false if response data is processed asynchronously, in which
|
| -// case ReadResponseData will be invoked when it is safe to continue
|
| -// reading more response data from the request.
|
| -bool AppCacheUpdateJob::URLFetcher::ConsumeResponseData(int bytes_read) {
|
| - DCHECK_GT(bytes_read, 0);
|
| - switch (fetch_type_) {
|
| - case MANIFEST_FETCH:
|
| - case MANIFEST_REFETCH:
|
| - manifest_data_.append(buffer_->data(), bytes_read);
|
| - break;
|
| - case URL_FETCH:
|
| - case MASTER_ENTRY_FETCH:
|
| - DCHECK(response_writer_.get());
|
| - response_writer_->WriteData(
|
| - buffer_.get(),
|
| - bytes_read,
|
| - base::Bind(&URLFetcher::OnWriteComplete, base::Unretained(this)));
|
| - return false; // wait for async write completion to continue reading
|
| - default:
|
| - NOTREACHED();
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -void AppCacheUpdateJob::URLFetcher::OnResponseCompleted() {
|
| - if (request_->status().is_success())
|
| - job_->MadeProgress();
|
| -
|
| - // Retry for 503s where retry-after is 0.
|
| - if (request_->status().is_success() &&
|
| - request_->GetResponseCode() == 503 &&
|
| - MaybeRetryRequest()) {
|
| - return;
|
| - }
|
| -
|
| - switch (fetch_type_) {
|
| - case MANIFEST_FETCH:
|
| - job_->HandleManifestFetchCompleted(this);
|
| - break;
|
| - case URL_FETCH:
|
| - job_->HandleUrlFetchCompleted(this);
|
| - break;
|
| - case MASTER_ENTRY_FETCH:
|
| - job_->HandleMasterEntryFetchCompleted(this);
|
| - break;
|
| - case MANIFEST_REFETCH:
|
| - job_->HandleManifestRefetchCompleted(this);
|
| - break;
|
| - default:
|
| - NOTREACHED();
|
| - }
|
| -
|
| - delete this;
|
| -}
|
| -
|
| -bool AppCacheUpdateJob::URLFetcher::MaybeRetryRequest() {
|
| - if (retry_503_attempts_ >= kMax503Retries ||
|
| - !request_->response_headers()->HasHeaderValue("retry-after", "0")) {
|
| - return false;
|
| - }
|
| - ++retry_503_attempts_;
|
| - result_ = UPDATE_OK;
|
| - request_ = job_->service_->request_context()->CreateRequest(
|
| - url_, net::DEFAULT_PRIORITY, this, NULL);
|
| - Start();
|
| - return true;
|
| -}
|
| -
|
| -AppCacheUpdateJob::AppCacheUpdateJob(AppCacheServiceImpl* service,
|
| - AppCacheGroup* group)
|
| - : service_(service),
|
| - manifest_url_(group->manifest_url()),
|
| - group_(group),
|
| - update_type_(UNKNOWN_TYPE),
|
| - internal_state_(FETCH_MANIFEST),
|
| - master_entries_completed_(0),
|
| - url_fetches_completed_(0),
|
| - manifest_fetcher_(NULL),
|
| - manifest_has_valid_mime_type_(false),
|
| - stored_state_(UNSTORED),
|
| - storage_(service->storage()) {
|
| - service_->AddObserver(this);
|
| -}
|
| -
|
| -AppCacheUpdateJob::~AppCacheUpdateJob() {
|
| - if (service_)
|
| - service_->RemoveObserver(this);
|
| - if (internal_state_ != COMPLETED)
|
| - Cancel();
|
| -
|
| - DCHECK(!manifest_fetcher_);
|
| - DCHECK(pending_url_fetches_.empty());
|
| - DCHECK(!inprogress_cache_.get());
|
| - DCHECK(pending_master_entries_.empty());
|
| - DCHECK(master_entry_fetches_.empty());
|
| -
|
| - if (group_)
|
| - group_->SetUpdateAppCacheStatus(AppCacheGroup::IDLE);
|
| -}
|
| -
|
| -void AppCacheUpdateJob::StartUpdate(AppCacheHost* host,
|
| - const GURL& new_master_resource) {
|
| - DCHECK(group_->update_job() == this);
|
| - DCHECK(!group_->is_obsolete());
|
| -
|
| - bool is_new_pending_master_entry = false;
|
| - if (!new_master_resource.is_empty()) {
|
| - DCHECK(new_master_resource == host->pending_master_entry_url());
|
| - DCHECK(!new_master_resource.has_ref());
|
| - DCHECK(new_master_resource.GetOrigin() == manifest_url_.GetOrigin());
|
| -
|
| - // Cannot add more to this update if already terminating.
|
| - if (IsTerminating()) {
|
| - group_->QueueUpdate(host, new_master_resource);
|
| - return;
|
| - }
|
| -
|
| - std::pair<PendingMasters::iterator, bool> ret =
|
| - pending_master_entries_.insert(
|
| - PendingMasters::value_type(new_master_resource, PendingHosts()));
|
| - is_new_pending_master_entry = ret.second;
|
| - ret.first->second.push_back(host);
|
| - host->AddObserver(this);
|
| - }
|
| -
|
| - // Notify host (if any) if already checking or downloading.
|
| - AppCacheGroup::UpdateAppCacheStatus update_status = group_->update_status();
|
| - if (update_status == AppCacheGroup::CHECKING ||
|
| - update_status == AppCacheGroup::DOWNLOADING) {
|
| - if (host) {
|
| - NotifySingleHost(host, APPCACHE_CHECKING_EVENT);
|
| - if (update_status == AppCacheGroup::DOWNLOADING)
|
| - NotifySingleHost(host, APPCACHE_DOWNLOADING_EVENT);
|
| -
|
| - // Add to fetch list or an existing entry if already fetched.
|
| - if (!new_master_resource.is_empty()) {
|
| - AddMasterEntryToFetchList(host, new_master_resource,
|
| - is_new_pending_master_entry);
|
| - }
|
| - }
|
| - return;
|
| - }
|
| -
|
| - // Begin update process for the group.
|
| - MadeProgress();
|
| - group_->SetUpdateAppCacheStatus(AppCacheGroup::CHECKING);
|
| - if (group_->HasCache()) {
|
| - update_type_ = UPGRADE_ATTEMPT;
|
| - NotifyAllAssociatedHosts(APPCACHE_CHECKING_EVENT);
|
| - } else {
|
| - update_type_ = CACHE_ATTEMPT;
|
| - DCHECK(host);
|
| - NotifySingleHost(host, APPCACHE_CHECKING_EVENT);
|
| - }
|
| -
|
| - if (!new_master_resource.is_empty()) {
|
| - AddMasterEntryToFetchList(host, new_master_resource,
|
| - is_new_pending_master_entry);
|
| - }
|
| -
|
| - FetchManifest(true);
|
| -}
|
| -
|
| -AppCacheResponseWriter* AppCacheUpdateJob::CreateResponseWriter() {
|
| - AppCacheResponseWriter* writer =
|
| - storage_->CreateResponseWriter(manifest_url_,
|
| - group_->group_id());
|
| - stored_response_ids_.push_back(writer->response_id());
|
| - return writer;
|
| -}
|
| -
|
| -void AppCacheUpdateJob::HandleCacheFailure(
|
| - const AppCacheErrorDetails& error_details,
|
| - ResultType result,
|
| - const GURL& failed_resource_url) {
|
| - // 6.9.4 cache failure steps 2-8.
|
| - DCHECK(internal_state_ != CACHE_FAILURE);
|
| - DCHECK(!error_details.message.empty());
|
| - DCHECK(result != UPDATE_OK);
|
| - internal_state_ = CACHE_FAILURE;
|
| - LogHistogramStats(result, failed_resource_url);
|
| - CancelAllUrlFetches();
|
| - CancelAllMasterEntryFetches(error_details);
|
| - NotifyAllError(error_details);
|
| - DiscardInprogressCache();
|
| - internal_state_ = COMPLETED;
|
| - DeleteSoon(); // To unwind the stack prior to deletion.
|
| -}
|
| -
|
| -void AppCacheUpdateJob::FetchManifest(bool is_first_fetch) {
|
| - DCHECK(!manifest_fetcher_);
|
| - manifest_fetcher_ = new URLFetcher(
|
| - manifest_url_,
|
| - is_first_fetch ? URLFetcher::MANIFEST_FETCH :
|
| - URLFetcher::MANIFEST_REFETCH,
|
| - this);
|
| -
|
| - // Add any necessary Http headers before sending fetch request.
|
| - if (is_first_fetch) {
|
| - AppCacheEntry* entry = (update_type_ == UPGRADE_ATTEMPT) ?
|
| - group_->newest_complete_cache()->GetEntry(manifest_url_) : NULL;
|
| - if (entry) {
|
| - // Asynchronously load response info for manifest from newest cache.
|
| - storage_->LoadResponseInfo(manifest_url_, group_->group_id(),
|
| - entry->response_id(), this);
|
| - } else {
|
| - manifest_fetcher_->Start();
|
| - }
|
| - } else {
|
| - DCHECK(internal_state_ == REFETCH_MANIFEST);
|
| - DCHECK(manifest_response_info_.get());
|
| - manifest_fetcher_->set_existing_response_headers(
|
| - manifest_response_info_->headers.get());
|
| - manifest_fetcher_->Start();
|
| - }
|
| -}
|
| -
|
| -
|
| -void AppCacheUpdateJob::HandleManifestFetchCompleted(
|
| - URLFetcher* fetcher) {
|
| - DCHECK_EQ(internal_state_, FETCH_MANIFEST);
|
| - DCHECK_EQ(manifest_fetcher_, fetcher);
|
| - manifest_fetcher_ = NULL;
|
| -
|
| - net::URLRequest* request = fetcher->request();
|
| - int response_code = -1;
|
| - bool is_valid_response_code = false;
|
| - if (request->status().is_success()) {
|
| - response_code = request->GetResponseCode();
|
| - is_valid_response_code = (response_code / 100 == 2);
|
| -
|
| - std::string mime_type;
|
| - request->GetMimeType(&mime_type);
|
| - manifest_has_valid_mime_type_ = (mime_type == "text/cache-manifest");
|
| - }
|
| -
|
| - if (is_valid_response_code) {
|
| - manifest_data_ = fetcher->manifest_data();
|
| - manifest_response_info_.reset(
|
| - new net::HttpResponseInfo(request->response_info()));
|
| - if (update_type_ == UPGRADE_ATTEMPT)
|
| - CheckIfManifestChanged(); // continues asynchronously
|
| - else
|
| - ContinueHandleManifestFetchCompleted(true);
|
| - } else if (response_code == 304 && update_type_ == UPGRADE_ATTEMPT) {
|
| - ContinueHandleManifestFetchCompleted(false);
|
| - } else if ((response_code == 404 || response_code == 410) &&
|
| - update_type_ == UPGRADE_ATTEMPT) {
|
| - storage_->MakeGroupObsolete(group_, this, response_code); // async
|
| - } else {
|
| - const char* kFormatString = "Manifest fetch failed (%d) %s";
|
| - std::string message = FormatUrlErrorMessage(
|
| - kFormatString, manifest_url_, fetcher->result(), response_code);
|
| - HandleCacheFailure(AppCacheErrorDetails(message,
|
| - appcache::APPCACHE_MANIFEST_ERROR,
|
| - manifest_url_,
|
| - response_code,
|
| - false /*is_cross_origin*/),
|
| - fetcher->result(),
|
| - GURL());
|
| - }
|
| -}
|
| -
|
| -void AppCacheUpdateJob::OnGroupMadeObsolete(AppCacheGroup* group,
|
| - bool success,
|
| - int response_code) {
|
| - DCHECK(master_entry_fetches_.empty());
|
| - CancelAllMasterEntryFetches(AppCacheErrorDetails(
|
| - "The cache has been made obsolete, "
|
| - "the manifest file returned 404 or 410",
|
| - appcache::APPCACHE_MANIFEST_ERROR,
|
| - GURL(),
|
| - response_code,
|
| - false /*is_cross_origin*/));
|
| - if (success) {
|
| - DCHECK(group->is_obsolete());
|
| - NotifyAllAssociatedHosts(APPCACHE_OBSOLETE_EVENT);
|
| - internal_state_ = COMPLETED;
|
| - MaybeCompleteUpdate();
|
| - } else {
|
| - // Treat failure to mark group obsolete as a cache failure.
|
| - HandleCacheFailure(AppCacheErrorDetails(
|
| - "Failed to mark the cache as obsolete",
|
| - APPCACHE_UNKNOWN_ERROR,
|
| - GURL(),
|
| - 0,
|
| - false /*is_cross_origin*/),
|
| - DB_ERROR,
|
| - GURL());
|
| - }
|
| -}
|
| -
|
| -void AppCacheUpdateJob::ContinueHandleManifestFetchCompleted(bool changed) {
|
| - DCHECK(internal_state_ == FETCH_MANIFEST);
|
| -
|
| - if (!changed) {
|
| - DCHECK(update_type_ == UPGRADE_ATTEMPT);
|
| - internal_state_ = NO_UPDATE;
|
| -
|
| - // Wait for pending master entries to download.
|
| - FetchMasterEntries();
|
| - MaybeCompleteUpdate(); // if not done, run async 6.9.4 step 7 substeps
|
| - return;
|
| - }
|
| -
|
| - Manifest manifest;
|
| - if (!ParseManifest(manifest_url_, manifest_data_.data(),
|
| - manifest_data_.length(),
|
| - manifest_has_valid_mime_type_ ?
|
| - PARSE_MANIFEST_ALLOWING_INTERCEPTS :
|
| - PARSE_MANIFEST_PER_STANDARD,
|
| - manifest)) {
|
| - const char* kFormatString = "Failed to parse manifest %s";
|
| - const std::string message = base::StringPrintf(kFormatString,
|
| - manifest_url_.spec().c_str());
|
| - HandleCacheFailure(
|
| - AppCacheErrorDetails(
|
| - message, APPCACHE_SIGNATURE_ERROR, GURL(), 0,
|
| - false /*is_cross_origin*/),
|
| - APPCACHE_MANIFEST_ERROR,
|
| - GURL());
|
| - VLOG(1) << message;
|
| - return;
|
| - }
|
| -
|
| - // Proceed with update process. Section 6.9.4 steps 8-20.
|
| - internal_state_ = DOWNLOADING;
|
| - inprogress_cache_ = new AppCache(storage_, storage_->NewCacheId());
|
| - BuildUrlFileList(manifest);
|
| - inprogress_cache_->InitializeWithManifest(&manifest);
|
| -
|
| - // Associate all pending master hosts with the newly created cache.
|
| - for (PendingMasters::iterator it = pending_master_entries_.begin();
|
| - it != pending_master_entries_.end(); ++it) {
|
| - PendingHosts& hosts = it->second;
|
| - for (PendingHosts::iterator host_it = hosts.begin();
|
| - host_it != hosts.end(); ++host_it) {
|
| - (*host_it)
|
| - ->AssociateIncompleteCache(inprogress_cache_.get(), manifest_url_);
|
| - }
|
| - }
|
| -
|
| - if (manifest.did_ignore_intercept_namespaces) {
|
| - // Must be done after associating all pending master hosts.
|
| - std::string message(
|
| - "Ignoring the INTERCEPT section of the application cache manifest "
|
| - "because the content type is not text/cache-manifest");
|
| - LogConsoleMessageToAll(message);
|
| - }
|
| -
|
| - group_->SetUpdateAppCacheStatus(AppCacheGroup::DOWNLOADING);
|
| - NotifyAllAssociatedHosts(APPCACHE_DOWNLOADING_EVENT);
|
| - FetchUrls();
|
| - FetchMasterEntries();
|
| - MaybeCompleteUpdate(); // if not done, continues when async fetches complete
|
| -}
|
| -
|
| -void AppCacheUpdateJob::HandleUrlFetchCompleted(URLFetcher* fetcher) {
|
| - DCHECK(internal_state_ == DOWNLOADING);
|
| -
|
| - net::URLRequest* request = fetcher->request();
|
| - const GURL& url = request->original_url();
|
| - pending_url_fetches_.erase(url);
|
| - NotifyAllProgress(url);
|
| - ++url_fetches_completed_;
|
| -
|
| - int response_code = request->status().is_success()
|
| - ? request->GetResponseCode()
|
| - : fetcher->redirect_response_code();
|
| -
|
| - AppCacheEntry& entry = url_file_list_.find(url)->second;
|
| -
|
| - if (response_code / 100 == 2) {
|
| - // Associate storage with the new entry.
|
| - DCHECK(fetcher->response_writer());
|
| - entry.set_response_id(fetcher->response_writer()->response_id());
|
| - entry.set_response_size(fetcher->response_writer()->amount_written());
|
| - if (!inprogress_cache_->AddOrModifyEntry(url, entry))
|
| - duplicate_response_ids_.push_back(entry.response_id());
|
| -
|
| - // TODO(michaeln): Check for <html manifest=xxx>
|
| - // See http://code.google.com/p/chromium/issues/detail?id=97930
|
| - // if (entry.IsMaster() && !(entry.IsExplicit() || fallback || intercept))
|
| - // if (!manifestAttribute) skip it
|
| -
|
| - // Foreign entries will be detected during cache selection.
|
| - // Note: 6.9.4, step 17.9 possible optimization: if resource is HTML or XML
|
| - // file whose root element is an html element with a manifest attribute
|
| - // whose value doesn't match the manifest url of the application cache
|
| - // being processed, mark the entry as being foreign.
|
| - } else {
|
| - VLOG(1) << "Request status: " << request->status().status()
|
| - << " error: " << request->status().error()
|
| - << " response code: " << response_code;
|
| - if (entry.IsExplicit() || entry.IsFallback() || entry.IsIntercept()) {
|
| - if (response_code == 304 && fetcher->existing_entry().has_response_id()) {
|
| - // Keep the existing response.
|
| - entry.set_response_id(fetcher->existing_entry().response_id());
|
| - entry.set_response_size(fetcher->existing_entry().response_size());
|
| - inprogress_cache_->AddOrModifyEntry(url, entry);
|
| - } else {
|
| - const char* kFormatString = "Resource fetch failed (%d) %s";
|
| - std::string message = FormatUrlErrorMessage(
|
| - kFormatString, url, fetcher->result(), response_code);
|
| - ResultType result = fetcher->result();
|
| - bool is_cross_origin = url.GetOrigin() != manifest_url_.GetOrigin();
|
| - switch (result) {
|
| - case DISKCACHE_ERROR:
|
| - HandleCacheFailure(
|
| - AppCacheErrorDetails(
|
| - message, APPCACHE_UNKNOWN_ERROR, GURL(), 0,
|
| - is_cross_origin),
|
| - result,
|
| - url);
|
| - break;
|
| - case NETWORK_ERROR:
|
| - HandleCacheFailure(
|
| - AppCacheErrorDetails(message, APPCACHE_RESOURCE_ERROR, url, 0,
|
| - is_cross_origin),
|
| - result,
|
| - url);
|
| - break;
|
| - default:
|
| - HandleCacheFailure(AppCacheErrorDetails(message,
|
| - APPCACHE_RESOURCE_ERROR,
|
| - url,
|
| - response_code,
|
| - is_cross_origin),
|
| - result,
|
| - url);
|
| - break;
|
| - }
|
| - return;
|
| - }
|
| - } else if (response_code == 404 || response_code == 410) {
|
| - // Entry is skipped. They are dropped from the cache.
|
| - } else if (update_type_ == UPGRADE_ATTEMPT &&
|
| - fetcher->existing_entry().has_response_id()) {
|
| - // Keep the existing response.
|
| - // TODO(michaeln): Not sure this is a good idea. This is spec compliant
|
| - // but the old resource may or may not be compatible with the new contents
|
| - // of the cache. Impossible to know one way or the other.
|
| - entry.set_response_id(fetcher->existing_entry().response_id());
|
| - entry.set_response_size(fetcher->existing_entry().response_size());
|
| - inprogress_cache_->AddOrModifyEntry(url, entry);
|
| - }
|
| - }
|
| -
|
| - // Fetch another URL now that one request has completed.
|
| - DCHECK(internal_state_ != CACHE_FAILURE);
|
| - FetchUrls();
|
| - MaybeCompleteUpdate();
|
| -}
|
| -
|
| -void AppCacheUpdateJob::HandleMasterEntryFetchCompleted(
|
| - URLFetcher* fetcher) {
|
| - DCHECK(internal_state_ == NO_UPDATE || internal_state_ == DOWNLOADING);
|
| -
|
| - // TODO(jennb): Handle downloads completing during cache failure when update
|
| - // no longer fetches master entries directly. For now, we cancel all pending
|
| - // master entry fetches when entering cache failure state so this will never
|
| - // be called in CACHE_FAILURE state.
|
| -
|
| - net::URLRequest* request = fetcher->request();
|
| - const GURL& url = request->original_url();
|
| - master_entry_fetches_.erase(url);
|
| - ++master_entries_completed_;
|
| -
|
| - int response_code = request->status().is_success()
|
| - ? request->GetResponseCode() : -1;
|
| -
|
| - PendingMasters::iterator found = pending_master_entries_.find(url);
|
| - DCHECK(found != pending_master_entries_.end());
|
| - PendingHosts& hosts = found->second;
|
| -
|
| - // Section 6.9.4. No update case: step 7.3, else step 22.
|
| - if (response_code / 100 == 2) {
|
| - // Add fetched master entry to the appropriate cache.
|
| - AppCache* cache = inprogress_cache_.get() ? inprogress_cache_.get()
|
| - : group_->newest_complete_cache();
|
| - DCHECK(fetcher->response_writer());
|
| - AppCacheEntry master_entry(AppCacheEntry::MASTER,
|
| - fetcher->response_writer()->response_id(),
|
| - fetcher->response_writer()->amount_written());
|
| - if (cache->AddOrModifyEntry(url, master_entry))
|
| - added_master_entries_.push_back(url);
|
| - else
|
| - duplicate_response_ids_.push_back(master_entry.response_id());
|
| -
|
| - // In no-update case, associate host with the newest cache.
|
| - if (!inprogress_cache_.get()) {
|
| - // TODO(michaeln): defer until the updated cache has been stored
|
| - DCHECK(cache == group_->newest_complete_cache());
|
| - for (PendingHosts::iterator host_it = hosts.begin();
|
| - host_it != hosts.end(); ++host_it) {
|
| - (*host_it)->AssociateCompleteCache(cache);
|
| - }
|
| - }
|
| - } else {
|
| - HostNotifier host_notifier;
|
| - for (PendingHosts::iterator host_it = hosts.begin();
|
| - host_it != hosts.end(); ++host_it) {
|
| - AppCacheHost* host = *host_it;
|
| - host_notifier.AddHost(host);
|
| -
|
| - // In downloading case, disassociate host from inprogress cache.
|
| - if (inprogress_cache_.get())
|
| - host->AssociateNoCache(GURL());
|
| -
|
| - host->RemoveObserver(this);
|
| - }
|
| - hosts.clear();
|
| -
|
| - const char* kFormatString = "Manifest fetch failed (%d) %s";
|
| - std::string message = FormatUrlErrorMessage(
|
| - kFormatString, request->url(), fetcher->result(), response_code);
|
| - host_notifier.SendErrorNotifications(
|
| - AppCacheErrorDetails(message,
|
| - appcache::APPCACHE_MANIFEST_ERROR,
|
| - request->url(),
|
| - response_code,
|
| - false /*is_cross_origin*/));
|
| -
|
| - // In downloading case, update result is different if all master entries
|
| - // failed vs. only some failing.
|
| - if (inprogress_cache_.get()) {
|
| - // Only count successful downloads to know if all master entries failed.
|
| - pending_master_entries_.erase(found);
|
| - --master_entries_completed_;
|
| -
|
| - // Section 6.9.4, step 22.3.
|
| - if (update_type_ == CACHE_ATTEMPT && pending_master_entries_.empty()) {
|
| - HandleCacheFailure(AppCacheErrorDetails(message,
|
| - appcache::APPCACHE_MANIFEST_ERROR,
|
| - request->url(),
|
| - response_code,
|
| - false /*is_cross_origin*/),
|
| - fetcher->result(),
|
| - GURL());
|
| - return;
|
| - }
|
| - }
|
| - }
|
| -
|
| - DCHECK(internal_state_ != CACHE_FAILURE);
|
| - FetchMasterEntries();
|
| - MaybeCompleteUpdate();
|
| -}
|
| -
|
| -void AppCacheUpdateJob::HandleManifestRefetchCompleted(
|
| - URLFetcher* fetcher) {
|
| - DCHECK(internal_state_ == REFETCH_MANIFEST);
|
| - DCHECK(manifest_fetcher_ == fetcher);
|
| - manifest_fetcher_ = NULL;
|
| -
|
| - net::URLRequest* request = fetcher->request();
|
| - int response_code = request->status().is_success()
|
| - ? request->GetResponseCode() : -1;
|
| - if (response_code == 304 || manifest_data_ == fetcher->manifest_data()) {
|
| - // Only need to store response in storage if manifest is not already
|
| - // an entry in the cache.
|
| - AppCacheEntry* entry = inprogress_cache_->GetEntry(manifest_url_);
|
| - if (entry) {
|
| - entry->add_types(AppCacheEntry::MANIFEST);
|
| - StoreGroupAndCache();
|
| - } else {
|
| - manifest_response_writer_.reset(CreateResponseWriter());
|
| - scoped_refptr<HttpResponseInfoIOBuffer> io_buffer(
|
| - new HttpResponseInfoIOBuffer(manifest_response_info_.release()));
|
| - manifest_response_writer_->WriteInfo(
|
| - io_buffer.get(),
|
| - base::Bind(&AppCacheUpdateJob::OnManifestInfoWriteComplete,
|
| - base::Unretained(this)));
|
| - }
|
| - } else {
|
| - VLOG(1) << "Request status: " << request->status().status()
|
| - << " error: " << request->status().error()
|
| - << " response code: " << response_code;
|
| - ScheduleUpdateRetry(kRerunDelayMs);
|
| - if (response_code == 200) {
|
| - HandleCacheFailure(AppCacheErrorDetails("Manifest changed during update",
|
| - APPCACHE_CHANGED_ERROR,
|
| - GURL(),
|
| - 0,
|
| - false /*is_cross_origin*/),
|
| - APPCACHE_MANIFEST_ERROR,
|
| - GURL());
|
| - } else {
|
| - const char* kFormatString = "Manifest re-fetch failed (%d) %s";
|
| - std::string message = FormatUrlErrorMessage(
|
| - kFormatString, manifest_url_, fetcher->result(), response_code);
|
| - HandleCacheFailure(AppCacheErrorDetails(message,
|
| - appcache::APPCACHE_MANIFEST_ERROR,
|
| - GURL(),
|
| - response_code,
|
| - false /*is_cross_origin*/),
|
| - fetcher->result(),
|
| - GURL());
|
| - }
|
| - }
|
| -}
|
| -
|
| -void AppCacheUpdateJob::OnManifestInfoWriteComplete(int result) {
|
| - if (result > 0) {
|
| - scoped_refptr<net::StringIOBuffer> io_buffer(
|
| - new net::StringIOBuffer(manifest_data_));
|
| - manifest_response_writer_->WriteData(
|
| - io_buffer.get(),
|
| - manifest_data_.length(),
|
| - base::Bind(&AppCacheUpdateJob::OnManifestDataWriteComplete,
|
| - base::Unretained(this)));
|
| - } else {
|
| - HandleCacheFailure(
|
| - AppCacheErrorDetails("Failed to write the manifest headers to storage",
|
| - APPCACHE_UNKNOWN_ERROR,
|
| - GURL(),
|
| - 0,
|
| - false /*is_cross_origin*/),
|
| - DISKCACHE_ERROR,
|
| - GURL());
|
| - }
|
| -}
|
| -
|
| -void AppCacheUpdateJob::OnManifestDataWriteComplete(int result) {
|
| - if (result > 0) {
|
| - AppCacheEntry entry(AppCacheEntry::MANIFEST,
|
| - manifest_response_writer_->response_id(),
|
| - manifest_response_writer_->amount_written());
|
| - if (!inprogress_cache_->AddOrModifyEntry(manifest_url_, entry))
|
| - duplicate_response_ids_.push_back(entry.response_id());
|
| - StoreGroupAndCache();
|
| - } else {
|
| - HandleCacheFailure(
|
| - AppCacheErrorDetails("Failed to write the manifest data to storage",
|
| - APPCACHE_UNKNOWN_ERROR,
|
| - GURL(),
|
| - 0,
|
| - false /*is_cross_origin*/),
|
| - DISKCACHE_ERROR,
|
| - GURL());
|
| - }
|
| -}
|
| -
|
| -void AppCacheUpdateJob::StoreGroupAndCache() {
|
| - DCHECK(stored_state_ == UNSTORED);
|
| - stored_state_ = STORING;
|
| - scoped_refptr<AppCache> newest_cache;
|
| - if (inprogress_cache_.get())
|
| - newest_cache.swap(inprogress_cache_);
|
| - else
|
| - newest_cache = group_->newest_complete_cache();
|
| - newest_cache->set_update_time(base::Time::Now());
|
| -
|
| - // TODO(michaeln): dcheck is fishing for clues to crbug/95101
|
| - DCHECK_EQ(manifest_url_, group_->manifest_url());
|
| - storage_->StoreGroupAndNewestCache(group_, newest_cache.get(), this);
|
| -}
|
| -
|
| -void AppCacheUpdateJob::OnGroupAndNewestCacheStored(AppCacheGroup* group,
|
| - AppCache* newest_cache,
|
| - bool success,
|
| - bool would_exceed_quota) {
|
| - DCHECK(stored_state_ == STORING);
|
| - if (success) {
|
| - stored_state_ = STORED;
|
| - MaybeCompleteUpdate(); // will definitely complete
|
| - } else {
|
| - stored_state_ = UNSTORED;
|
| -
|
| - // Restore inprogress_cache_ to get the proper events delivered
|
| - // and the proper cleanup to occur.
|
| - if (newest_cache != group->newest_complete_cache())
|
| - inprogress_cache_ = newest_cache;
|
| -
|
| - ResultType result = DB_ERROR;
|
| - AppCacheErrorReason reason = APPCACHE_UNKNOWN_ERROR;
|
| - std::string message("Failed to commit new cache to storage");
|
| - if (would_exceed_quota) {
|
| - message.append(", would exceed quota");
|
| - result = APPCACHE_QUOTA_ERROR;
|
| - reason = appcache::APPCACHE_QUOTA_ERROR;
|
| - }
|
| - HandleCacheFailure(
|
| - AppCacheErrorDetails(message, reason, GURL(), 0,
|
| - false /*is_cross_origin*/),
|
| - result,
|
| - GURL());
|
| - }
|
| -}
|
| -
|
| -void AppCacheUpdateJob::NotifySingleHost(AppCacheHost* host,
|
| - AppCacheEventID event_id) {
|
| - std::vector<int> ids(1, host->host_id());
|
| - host->frontend()->OnEventRaised(ids, event_id);
|
| -}
|
| -
|
| -void AppCacheUpdateJob::NotifyAllAssociatedHosts(AppCacheEventID event_id) {
|
| - HostNotifier host_notifier;
|
| - AddAllAssociatedHostsToNotifier(&host_notifier);
|
| - host_notifier.SendNotifications(event_id);
|
| -}
|
| -
|
| -void AppCacheUpdateJob::NotifyAllProgress(const GURL& url) {
|
| - HostNotifier host_notifier;
|
| - AddAllAssociatedHostsToNotifier(&host_notifier);
|
| - host_notifier.SendProgressNotifications(
|
| - url, url_file_list_.size(), url_fetches_completed_);
|
| -}
|
| -
|
| -void AppCacheUpdateJob::NotifyAllFinalProgress() {
|
| - DCHECK(url_file_list_.size() == url_fetches_completed_);
|
| - NotifyAllProgress(GURL());
|
| -}
|
| -
|
| -void AppCacheUpdateJob::NotifyAllError(const AppCacheErrorDetails& details) {
|
| - HostNotifier host_notifier;
|
| - AddAllAssociatedHostsToNotifier(&host_notifier);
|
| - host_notifier.SendErrorNotifications(details);
|
| -}
|
| -
|
| -void AppCacheUpdateJob::LogConsoleMessageToAll(const std::string& message) {
|
| - HostNotifier host_notifier;
|
| - AddAllAssociatedHostsToNotifier(&host_notifier);
|
| - host_notifier.SendLogMessage(message);
|
| -}
|
| -
|
| -void AppCacheUpdateJob::AddAllAssociatedHostsToNotifier(
|
| - HostNotifier* host_notifier) {
|
| - // Collect hosts so we only send one notification per frontend.
|
| - // A host can only be associated with a single cache so no need to worry
|
| - // about duplicate hosts being added to the notifier.
|
| - if (inprogress_cache_.get()) {
|
| - DCHECK(internal_state_ == DOWNLOADING || internal_state_ == CACHE_FAILURE);
|
| - host_notifier->AddHosts(inprogress_cache_->associated_hosts());
|
| - }
|
| -
|
| - AppCacheGroup::Caches old_caches = group_->old_caches();
|
| - for (AppCacheGroup::Caches::const_iterator it = old_caches.begin();
|
| - it != old_caches.end(); ++it) {
|
| - host_notifier->AddHosts((*it)->associated_hosts());
|
| - }
|
| -
|
| - AppCache* newest_cache = group_->newest_complete_cache();
|
| - if (newest_cache)
|
| - host_notifier->AddHosts(newest_cache->associated_hosts());
|
| -}
|
| -
|
| -void AppCacheUpdateJob::OnDestructionImminent(AppCacheHost* host) {
|
| - // The host is about to be deleted; remove from our collection.
|
| - PendingMasters::iterator found =
|
| - pending_master_entries_.find(host->pending_master_entry_url());
|
| - DCHECK(found != pending_master_entries_.end());
|
| - PendingHosts& hosts = found->second;
|
| - PendingHosts::iterator it = std::find(hosts.begin(), hosts.end(), host);
|
| - DCHECK(it != hosts.end());
|
| - hosts.erase(it);
|
| -}
|
| -
|
| -void AppCacheUpdateJob::OnServiceReinitialized(
|
| - AppCacheStorageReference* old_storage_ref) {
|
| - // We continue to use the disabled instance, but arrange for its
|
| - // deletion when its no longer needed.
|
| - if (old_storage_ref->storage() == storage_)
|
| - disabled_storage_reference_ = old_storage_ref;
|
| -}
|
| -
|
| -void AppCacheUpdateJob::CheckIfManifestChanged() {
|
| - DCHECK(update_type_ == UPGRADE_ATTEMPT);
|
| - AppCacheEntry* entry = NULL;
|
| - if (group_->newest_complete_cache())
|
| - entry = group_->newest_complete_cache()->GetEntry(manifest_url_);
|
| - if (!entry) {
|
| - // TODO(michaeln): This is just a bandaid to avoid a crash.
|
| - // http://code.google.com/p/chromium/issues/detail?id=95101
|
| - if (service_->storage() == storage_) {
|
| - // Use a local variable because service_ is reset in HandleCacheFailure.
|
| - AppCacheServiceImpl* service = service_;
|
| - HandleCacheFailure(
|
| - AppCacheErrorDetails("Manifest entry not found in existing cache",
|
| - APPCACHE_UNKNOWN_ERROR,
|
| - GURL(),
|
| - 0,
|
| - false /*is_cross_origin*/),
|
| - DB_ERROR,
|
| - GURL());
|
| - AppCacheHistograms::AddMissingManifestEntrySample();
|
| - service->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
|
| - }
|
| - return;
|
| - }
|
| -
|
| - // Load manifest data from storage to compare against fetched manifest.
|
| - manifest_response_reader_.reset(
|
| - storage_->CreateResponseReader(manifest_url_,
|
| - group_->group_id(),
|
| - entry->response_id()));
|
| - read_manifest_buffer_ = new net::IOBuffer(kBufferSize);
|
| - manifest_response_reader_->ReadData(
|
| - read_manifest_buffer_.get(),
|
| - kBufferSize,
|
| - base::Bind(&AppCacheUpdateJob::OnManifestDataReadComplete,
|
| - base::Unretained(this))); // async read
|
| -}
|
| -
|
| -void AppCacheUpdateJob::OnManifestDataReadComplete(int result) {
|
| - if (result > 0) {
|
| - loaded_manifest_data_.append(read_manifest_buffer_->data(), result);
|
| - manifest_response_reader_->ReadData(
|
| - read_manifest_buffer_.get(),
|
| - kBufferSize,
|
| - base::Bind(&AppCacheUpdateJob::OnManifestDataReadComplete,
|
| - base::Unretained(this))); // read more
|
| - } else {
|
| - read_manifest_buffer_ = NULL;
|
| - manifest_response_reader_.reset();
|
| - ContinueHandleManifestFetchCompleted(
|
| - result < 0 || manifest_data_ != loaded_manifest_data_);
|
| - }
|
| -}
|
| -
|
| -void AppCacheUpdateJob::BuildUrlFileList(const Manifest& manifest) {
|
| - for (base::hash_set<std::string>::const_iterator it =
|
| - manifest.explicit_urls.begin();
|
| - it != manifest.explicit_urls.end(); ++it) {
|
| - AddUrlToFileList(GURL(*it), AppCacheEntry::EXPLICIT);
|
| - }
|
| -
|
| - const std::vector<Namespace>& intercepts =
|
| - manifest.intercept_namespaces;
|
| - for (std::vector<Namespace>::const_iterator it = intercepts.begin();
|
| - it != intercepts.end(); ++it) {
|
| - int flags = AppCacheEntry::INTERCEPT;
|
| - if (it->is_executable)
|
| - flags |= AppCacheEntry::EXECUTABLE;
|
| - AddUrlToFileList(it->target_url, flags);
|
| - }
|
| -
|
| - const std::vector<Namespace>& fallbacks =
|
| - manifest.fallback_namespaces;
|
| - for (std::vector<Namespace>::const_iterator it = fallbacks.begin();
|
| - it != fallbacks.end(); ++it) {
|
| - AddUrlToFileList(it->target_url, AppCacheEntry::FALLBACK);
|
| - }
|
| -
|
| - // Add all master entries from newest complete cache.
|
| - if (update_type_ == UPGRADE_ATTEMPT) {
|
| - const AppCache::EntryMap& entries =
|
| - group_->newest_complete_cache()->entries();
|
| - for (AppCache::EntryMap::const_iterator it = entries.begin();
|
| - it != entries.end(); ++it) {
|
| - const AppCacheEntry& entry = it->second;
|
| - if (entry.IsMaster())
|
| - AddUrlToFileList(it->first, AppCacheEntry::MASTER);
|
| - }
|
| - }
|
| -}
|
| -
|
| -void AppCacheUpdateJob::AddUrlToFileList(const GURL& url, int type) {
|
| - std::pair<AppCache::EntryMap::iterator, bool> ret = url_file_list_.insert(
|
| - AppCache::EntryMap::value_type(url, AppCacheEntry(type)));
|
| -
|
| - if (ret.second)
|
| - urls_to_fetch_.push_back(UrlToFetch(url, false, NULL));
|
| - else
|
| - ret.first->second.add_types(type); // URL already exists. Merge types.
|
| -}
|
| -
|
| -void AppCacheUpdateJob::FetchUrls() {
|
| - DCHECK(internal_state_ == DOWNLOADING);
|
| -
|
| - // Fetch each URL in the list according to section 6.9.4 step 17.1-17.3.
|
| - // Fetch up to the concurrent limit. Other fetches will be triggered as each
|
| - // each fetch completes.
|
| - while (pending_url_fetches_.size() < kMaxConcurrentUrlFetches &&
|
| - !urls_to_fetch_.empty()) {
|
| - UrlToFetch url_to_fetch = urls_to_fetch_.front();
|
| - urls_to_fetch_.pop_front();
|
| -
|
| - AppCache::EntryMap::iterator it = url_file_list_.find(url_to_fetch.url);
|
| - DCHECK(it != url_file_list_.end());
|
| - AppCacheEntry& entry = it->second;
|
| - if (ShouldSkipUrlFetch(entry)) {
|
| - NotifyAllProgress(url_to_fetch.url);
|
| - ++url_fetches_completed_;
|
| - } else if (AlreadyFetchedEntry(url_to_fetch.url, entry.types())) {
|
| - NotifyAllProgress(url_to_fetch.url);
|
| - ++url_fetches_completed_; // saved a URL request
|
| - } else if (!url_to_fetch.storage_checked &&
|
| - MaybeLoadFromNewestCache(url_to_fetch.url, entry)) {
|
| - // Continues asynchronously after data is loaded from newest cache.
|
| - } else {
|
| - URLFetcher* fetcher = new URLFetcher(
|
| - url_to_fetch.url, URLFetcher::URL_FETCH, this);
|
| - if (url_to_fetch.existing_response_info.get()) {
|
| - DCHECK(group_->newest_complete_cache());
|
| - AppCacheEntry* existing_entry =
|
| - group_->newest_complete_cache()->GetEntry(url_to_fetch.url);
|
| - DCHECK(existing_entry);
|
| - DCHECK(existing_entry->response_id() ==
|
| - url_to_fetch.existing_response_info->response_id());
|
| - fetcher->set_existing_response_headers(
|
| - url_to_fetch.existing_response_info->http_response_info()->headers
|
| - .get());
|
| - fetcher->set_existing_entry(*existing_entry);
|
| - }
|
| - fetcher->Start();
|
| - pending_url_fetches_.insert(
|
| - PendingUrlFetches::value_type(url_to_fetch.url, fetcher));
|
| - }
|
| - }
|
| -}
|
| -
|
| -void AppCacheUpdateJob::CancelAllUrlFetches() {
|
| - // Cancel any pending URL requests.
|
| - for (PendingUrlFetches::iterator it = pending_url_fetches_.begin();
|
| - it != pending_url_fetches_.end(); ++it) {
|
| - delete it->second;
|
| - }
|
| -
|
| - url_fetches_completed_ +=
|
| - pending_url_fetches_.size() + urls_to_fetch_.size();
|
| - pending_url_fetches_.clear();
|
| - urls_to_fetch_.clear();
|
| -}
|
| -
|
| -bool AppCacheUpdateJob::ShouldSkipUrlFetch(const AppCacheEntry& entry) {
|
| - // 6.6.4 Step 17
|
| - // If the resource URL being processed was flagged as neither an
|
| - // "explicit entry" nor or a "fallback entry", then the user agent
|
| - // may skip this URL.
|
| - if (entry.IsExplicit() || entry.IsFallback() || entry.IsIntercept())
|
| - return false;
|
| -
|
| - // TODO(jennb): decide if entry should be skipped to expire it from cache
|
| - return false;
|
| -}
|
| -
|
| -bool AppCacheUpdateJob::AlreadyFetchedEntry(const GURL& url,
|
| - int entry_type) {
|
| - DCHECK(internal_state_ == DOWNLOADING || internal_state_ == NO_UPDATE);
|
| - AppCacheEntry* existing =
|
| - inprogress_cache_.get() ? inprogress_cache_->GetEntry(url)
|
| - : group_->newest_complete_cache()->GetEntry(url);
|
| - if (existing) {
|
| - existing->add_types(entry_type);
|
| - return true;
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -void AppCacheUpdateJob::AddMasterEntryToFetchList(AppCacheHost* host,
|
| - const GURL& url,
|
| - bool is_new) {
|
| - DCHECK(!IsTerminating());
|
| -
|
| - if (internal_state_ == DOWNLOADING || internal_state_ == NO_UPDATE) {
|
| - AppCache* cache;
|
| - if (inprogress_cache_.get()) {
|
| - // always associate
|
| - host->AssociateIncompleteCache(inprogress_cache_.get(), manifest_url_);
|
| - cache = inprogress_cache_.get();
|
| - } else {
|
| - cache = group_->newest_complete_cache();
|
| - }
|
| -
|
| - // Update existing entry if it has already been fetched.
|
| - AppCacheEntry* entry = cache->GetEntry(url);
|
| - if (entry) {
|
| - entry->add_types(AppCacheEntry::MASTER);
|
| - if (internal_state_ == NO_UPDATE && !inprogress_cache_.get()) {
|
| - // only associate if have entry
|
| - host->AssociateCompleteCache(cache);
|
| - }
|
| - if (is_new)
|
| - ++master_entries_completed_; // pretend fetching completed
|
| - return;
|
| - }
|
| - }
|
| -
|
| - // Add to fetch list if not already fetching.
|
| - if (master_entry_fetches_.find(url) == master_entry_fetches_.end()) {
|
| - master_entries_to_fetch_.insert(url);
|
| - if (internal_state_ == DOWNLOADING || internal_state_ == NO_UPDATE)
|
| - FetchMasterEntries();
|
| - }
|
| -}
|
| -
|
| -void AppCacheUpdateJob::FetchMasterEntries() {
|
| - DCHECK(internal_state_ == NO_UPDATE || internal_state_ == DOWNLOADING);
|
| -
|
| - // Fetch each master entry in the list, up to the concurrent limit.
|
| - // Additional fetches will be triggered as each fetch completes.
|
| - while (master_entry_fetches_.size() < kMaxConcurrentUrlFetches &&
|
| - !master_entries_to_fetch_.empty()) {
|
| - const GURL& url = *master_entries_to_fetch_.begin();
|
| -
|
| - if (AlreadyFetchedEntry(url, AppCacheEntry::MASTER)) {
|
| - ++master_entries_completed_; // saved a URL request
|
| -
|
| - // In no update case, associate hosts to newest cache in group
|
| - // now that master entry has been "successfully downloaded".
|
| - if (internal_state_ == NO_UPDATE) {
|
| - // TODO(michaeln): defer until the updated cache has been stored.
|
| - DCHECK(!inprogress_cache_.get());
|
| - AppCache* cache = group_->newest_complete_cache();
|
| - PendingMasters::iterator found = pending_master_entries_.find(url);
|
| - DCHECK(found != pending_master_entries_.end());
|
| - PendingHosts& hosts = found->second;
|
| - for (PendingHosts::iterator host_it = hosts.begin();
|
| - host_it != hosts.end(); ++host_it) {
|
| - (*host_it)->AssociateCompleteCache(cache);
|
| - }
|
| - }
|
| - } else {
|
| - URLFetcher* fetcher = new URLFetcher(
|
| - url, URLFetcher::MASTER_ENTRY_FETCH, this);
|
| - fetcher->Start();
|
| - master_entry_fetches_.insert(PendingUrlFetches::value_type(url, fetcher));
|
| - }
|
| -
|
| - master_entries_to_fetch_.erase(master_entries_to_fetch_.begin());
|
| - }
|
| -}
|
| -
|
| -void AppCacheUpdateJob::CancelAllMasterEntryFetches(
|
| - const AppCacheErrorDetails& error_details) {
|
| - // For now, cancel all in-progress fetches for master entries and pretend
|
| - // all master entries fetches have completed.
|
| - // TODO(jennb): Delete this when update no longer fetches master entries
|
| - // directly.
|
| -
|
| - // Cancel all in-progress fetches.
|
| - for (PendingUrlFetches::iterator it = master_entry_fetches_.begin();
|
| - it != master_entry_fetches_.end(); ++it) {
|
| - delete it->second;
|
| - master_entries_to_fetch_.insert(it->first); // back in unfetched list
|
| - }
|
| - master_entry_fetches_.clear();
|
| -
|
| - master_entries_completed_ += master_entries_to_fetch_.size();
|
| -
|
| - // Cache failure steps, step 2.
|
| - // Pretend all master entries that have not yet been fetched have completed
|
| - // downloading. Unassociate hosts from any appcache and send ERROR event.
|
| - HostNotifier host_notifier;
|
| - while (!master_entries_to_fetch_.empty()) {
|
| - const GURL& url = *master_entries_to_fetch_.begin();
|
| - PendingMasters::iterator found = pending_master_entries_.find(url);
|
| - DCHECK(found != pending_master_entries_.end());
|
| - PendingHosts& hosts = found->second;
|
| - for (PendingHosts::iterator host_it = hosts.begin();
|
| - host_it != hosts.end(); ++host_it) {
|
| - AppCacheHost* host = *host_it;
|
| - host->AssociateNoCache(GURL());
|
| - host_notifier.AddHost(host);
|
| - host->RemoveObserver(this);
|
| - }
|
| - hosts.clear();
|
| -
|
| - master_entries_to_fetch_.erase(master_entries_to_fetch_.begin());
|
| - }
|
| - host_notifier.SendErrorNotifications(error_details);
|
| -}
|
| -
|
| -bool AppCacheUpdateJob::MaybeLoadFromNewestCache(const GURL& url,
|
| - AppCacheEntry& entry) {
|
| - if (update_type_ != UPGRADE_ATTEMPT)
|
| - return false;
|
| -
|
| - AppCache* newest = group_->newest_complete_cache();
|
| - AppCacheEntry* copy_me = newest->GetEntry(url);
|
| - if (!copy_me || !copy_me->has_response_id())
|
| - return false;
|
| -
|
| - // Load HTTP headers for entry from newest cache.
|
| - loading_responses_.insert(
|
| - LoadingResponses::value_type(copy_me->response_id(), url));
|
| - storage_->LoadResponseInfo(manifest_url_, group_->group_id(),
|
| - copy_me->response_id(),
|
| - this);
|
| - // Async: wait for OnResponseInfoLoaded to complete.
|
| - return true;
|
| -}
|
| -
|
| -void AppCacheUpdateJob::OnResponseInfoLoaded(
|
| - AppCacheResponseInfo* response_info, int64 response_id) {
|
| - const net::HttpResponseInfo* http_info = response_info ?
|
| - response_info->http_response_info() : NULL;
|
| -
|
| - // Needed response info for a manifest fetch request.
|
| - if (internal_state_ == FETCH_MANIFEST) {
|
| - if (http_info)
|
| - manifest_fetcher_->set_existing_response_headers(
|
| - http_info->headers.get());
|
| - manifest_fetcher_->Start();
|
| - return;
|
| - }
|
| -
|
| - LoadingResponses::iterator found = loading_responses_.find(response_id);
|
| - DCHECK(found != loading_responses_.end());
|
| - const GURL& url = found->second;
|
| -
|
| - if (!http_info) {
|
| - LoadFromNewestCacheFailed(url, NULL); // no response found
|
| - } else {
|
| - // Check if response can be re-used according to HTTP caching semantics.
|
| - // Responses with a "vary" header get treated as expired.
|
| - const std::string name = "vary";
|
| - std::string value;
|
| - void* iter = NULL;
|
| - if (!http_info->headers.get() ||
|
| - http_info->headers->RequiresValidation(http_info->request_time,
|
| - http_info->response_time,
|
| - base::Time::Now()) ||
|
| - http_info->headers->EnumerateHeader(&iter, name, &value)) {
|
| - LoadFromNewestCacheFailed(url, response_info);
|
| - } else {
|
| - DCHECK(group_->newest_complete_cache());
|
| - AppCacheEntry* copy_me = group_->newest_complete_cache()->GetEntry(url);
|
| - DCHECK(copy_me);
|
| - DCHECK(copy_me->response_id() == response_id);
|
| -
|
| - AppCache::EntryMap::iterator it = url_file_list_.find(url);
|
| - DCHECK(it != url_file_list_.end());
|
| - AppCacheEntry& entry = it->second;
|
| - entry.set_response_id(response_id);
|
| - entry.set_response_size(copy_me->response_size());
|
| - inprogress_cache_->AddOrModifyEntry(url, entry);
|
| - NotifyAllProgress(url);
|
| - ++url_fetches_completed_;
|
| - }
|
| - }
|
| - loading_responses_.erase(found);
|
| -
|
| - MaybeCompleteUpdate();
|
| -}
|
| -
|
| -void AppCacheUpdateJob::LoadFromNewestCacheFailed(
|
| - const GURL& url, AppCacheResponseInfo* response_info) {
|
| - if (internal_state_ == CACHE_FAILURE)
|
| - return;
|
| -
|
| - // Re-insert url at front of fetch list. Indicate storage has been checked.
|
| - urls_to_fetch_.push_front(UrlToFetch(url, true, response_info));
|
| - FetchUrls();
|
| -}
|
| -
|
| -void AppCacheUpdateJob::MaybeCompleteUpdate() {
|
| - DCHECK(internal_state_ != CACHE_FAILURE);
|
| -
|
| - // Must wait for any pending master entries or url fetches to complete.
|
| - if (master_entries_completed_ != pending_master_entries_.size() ||
|
| - url_fetches_completed_ != url_file_list_.size()) {
|
| - DCHECK(internal_state_ != COMPLETED);
|
| - return;
|
| - }
|
| -
|
| - switch (internal_state_) {
|
| - case NO_UPDATE:
|
| - if (master_entries_completed_ > 0) {
|
| - switch (stored_state_) {
|
| - case UNSTORED:
|
| - StoreGroupAndCache();
|
| - return;
|
| - case STORING:
|
| - return;
|
| - case STORED:
|
| - break;
|
| - }
|
| - }
|
| - // 6.9.4 steps 7.3-7.7.
|
| - NotifyAllAssociatedHosts(APPCACHE_NO_UPDATE_EVENT);
|
| - DiscardDuplicateResponses();
|
| - internal_state_ = COMPLETED;
|
| - break;
|
| - case DOWNLOADING:
|
| - internal_state_ = REFETCH_MANIFEST;
|
| - FetchManifest(false);
|
| - break;
|
| - case REFETCH_MANIFEST:
|
| - DCHECK(stored_state_ == STORED);
|
| - NotifyAllFinalProgress();
|
| - if (update_type_ == CACHE_ATTEMPT)
|
| - NotifyAllAssociatedHosts(APPCACHE_CACHED_EVENT);
|
| - else
|
| - NotifyAllAssociatedHosts(APPCACHE_UPDATE_READY_EVENT);
|
| - DiscardDuplicateResponses();
|
| - internal_state_ = COMPLETED;
|
| - LogHistogramStats(UPDATE_OK, GURL());
|
| - break;
|
| - case CACHE_FAILURE:
|
| - NOTREACHED(); // See HandleCacheFailure
|
| - break;
|
| - default:
|
| - break;
|
| - }
|
| -
|
| - // Let the stack unwind before deletion to make it less risky as this
|
| - // method is called from multiple places in this file.
|
| - if (internal_state_ == COMPLETED)
|
| - DeleteSoon();
|
| -}
|
| -
|
| -void AppCacheUpdateJob::ScheduleUpdateRetry(int delay_ms) {
|
| - // TODO(jennb): post a delayed task with the "same parameters" as this job
|
| - // to retry the update at a later time. Need group, URLs of pending master
|
| - // entries and their hosts.
|
| -}
|
| -
|
| -void AppCacheUpdateJob::Cancel() {
|
| - internal_state_ = CANCELLED;
|
| -
|
| - LogHistogramStats(CANCELLED_ERROR, GURL());
|
| -
|
| - if (manifest_fetcher_) {
|
| - delete manifest_fetcher_;
|
| - manifest_fetcher_ = NULL;
|
| - }
|
| -
|
| - for (PendingUrlFetches::iterator it = pending_url_fetches_.begin();
|
| - it != pending_url_fetches_.end(); ++it) {
|
| - delete it->second;
|
| - }
|
| - pending_url_fetches_.clear();
|
| -
|
| - for (PendingUrlFetches::iterator it = master_entry_fetches_.begin();
|
| - it != master_entry_fetches_.end(); ++it) {
|
| - delete it->second;
|
| - }
|
| - master_entry_fetches_.clear();
|
| -
|
| - ClearPendingMasterEntries();
|
| - DiscardInprogressCache();
|
| -
|
| - // Delete response writer to avoid any callbacks.
|
| - if (manifest_response_writer_)
|
| - manifest_response_writer_.reset();
|
| -
|
| - storage_->CancelDelegateCallbacks(this);
|
| -}
|
| -
|
| -void AppCacheUpdateJob::ClearPendingMasterEntries() {
|
| - for (PendingMasters::iterator it = pending_master_entries_.begin();
|
| - it != pending_master_entries_.end(); ++it) {
|
| - PendingHosts& hosts = it->second;
|
| - for (PendingHosts::iterator host_it = hosts.begin();
|
| - host_it != hosts.end(); ++host_it) {
|
| - (*host_it)->RemoveObserver(this);
|
| - }
|
| - }
|
| -
|
| - pending_master_entries_.clear();
|
| -}
|
| -
|
| -void AppCacheUpdateJob::DiscardInprogressCache() {
|
| - if (stored_state_ == STORING) {
|
| - // We can make no assumptions about whether the StoreGroupAndCacheTask
|
| - // actually completed or not. This condition should only be reachable
|
| - // during shutdown. Free things up and return to do no harm.
|
| - inprogress_cache_ = NULL;
|
| - added_master_entries_.clear();
|
| - return;
|
| - }
|
| -
|
| - storage_->DoomResponses(manifest_url_, stored_response_ids_);
|
| -
|
| - if (!inprogress_cache_.get()) {
|
| - // We have to undo the changes we made, if any, to the existing cache.
|
| - if (group_ && group_->newest_complete_cache()) {
|
| - for (std::vector<GURL>::iterator iter = added_master_entries_.begin();
|
| - iter != added_master_entries_.end(); ++iter) {
|
| - group_->newest_complete_cache()->RemoveEntry(*iter);
|
| - }
|
| - }
|
| - added_master_entries_.clear();
|
| - return;
|
| - }
|
| -
|
| - AppCache::AppCacheHosts& hosts = inprogress_cache_->associated_hosts();
|
| - while (!hosts.empty())
|
| - (*hosts.begin())->AssociateNoCache(GURL());
|
| -
|
| - inprogress_cache_ = NULL;
|
| - added_master_entries_.clear();
|
| -}
|
| -
|
| -void AppCacheUpdateJob::DiscardDuplicateResponses() {
|
| - storage_->DoomResponses(manifest_url_, duplicate_response_ids_);
|
| -}
|
| -
|
| -void AppCacheUpdateJob::LogHistogramStats(
|
| - ResultType result, const GURL& failed_resource_url) {
|
| - AppCacheHistograms::CountUpdateJobResult(result, manifest_url_.GetOrigin());
|
| - if (result == UPDATE_OK)
|
| - return;
|
| -
|
| - int percent_complete = 0;
|
| - if (url_file_list_.size() > 0) {
|
| - size_t actual_fetches_completed = url_fetches_completed_;
|
| - if (!failed_resource_url.is_empty() && actual_fetches_completed)
|
| - --actual_fetches_completed;
|
| - percent_complete = (static_cast<double>(actual_fetches_completed) /
|
| - static_cast<double>(url_file_list_.size())) * 100.0;
|
| - percent_complete = std::min(percent_complete, 99);
|
| - }
|
| -
|
| - bool was_making_progress =
|
| - base::Time::Now() - last_progress_time_ <
|
| - base::TimeDelta::FromMinutes(5);
|
| -
|
| - bool off_origin_resource_failure =
|
| - !failed_resource_url.is_empty() &&
|
| - (failed_resource_url.GetOrigin() != manifest_url_.GetOrigin());
|
| -
|
| - AppCacheHistograms::LogUpdateFailureStats(
|
| - manifest_url_.GetOrigin(),
|
| - percent_complete,
|
| - was_making_progress,
|
| - off_origin_resource_failure);
|
| -}
|
| -
|
| -void AppCacheUpdateJob::DeleteSoon() {
|
| - ClearPendingMasterEntries();
|
| - manifest_response_writer_.reset();
|
| - storage_->CancelDelegateCallbacks(this);
|
| - service_->RemoveObserver(this);
|
| - service_ = NULL;
|
| -
|
| - // Break the connection with the group so the group cannot call delete
|
| - // on this object after we've posted a task to delete ourselves.
|
| - group_->SetUpdateAppCacheStatus(AppCacheGroup::IDLE);
|
| - group_ = NULL;
|
| -
|
| - base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
|
| -}
|
| -
|
| -} // namespace appcache
|
|
|