}
}
- void SendNotifications(EventID event_id) {
+ void SendNotifications(AppCacheEventID event_id) {
for (NotifyHostMap::iterator it = hosts_to_notify.begin();
it != hosts_to_notify.end(); ++it) {
AppCacheFrontend* frontend = it->first;
}
}
- void SendErrorNotifications(const std::string& error_message) {
- DCHECK(!error_message.empty());
+ 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, error_message);
+ 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);
+ }
}
}
retry_503_attempts_(0),
buffer_(new net::IOBuffer(kBufferSize)),
request_(job->service_->request_context()
- ->CreateRequest(url, net::DEFAULT_PRIORITY, this)),
- result_(UPDATE_OK) {}
+ ->CreateRequest(url, net::DEFAULT_PRIORITY, this, NULL)),
+ result_(UPDATE_OK),
+ redirect_response_code_(-1) {}
AppCacheUpdateJob::URLFetcher::~URLFetcher() {
}
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();
net::URLRequest *request) {
DCHECK(request == request_);
int response_code = -1;
- if (request->status().is_success())
+ 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
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();
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;
}
}
}
- if (data_consumed && !request->status().is_io_pending())
+ if (data_consumed && !request->status().is_io_pending()) {
+ DCHECK_EQ(UPDATE_OK, result_);
OnResponseCompleted();
+ }
}
void AppCacheUpdateJob::URLFetcher::AddConditionalHeaders(
}
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 &&
return false;
}
++retry_503_attempts_;
+ result_ = UPDATE_OK;
request_ = job_->service_->request_context()->CreateRequest(
- url_, net::DEFAULT_PRIORITY, this);
+ url_, net::DEFAULT_PRIORITY, this, NULL);
Start();
return true;
}
-AppCacheUpdateJob::AppCacheUpdateJob(AppCacheService* service,
+AppCacheUpdateJob::AppCacheUpdateJob(AppCacheServiceImpl* service,
AppCacheGroup* group)
: service_(service),
manifest_url_(group->manifest_url()),
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);
DCHECK(master_entry_fetches_.empty());
if (group_)
- group_->SetUpdateStatus(AppCacheGroup::IDLE);
+ group_->SetUpdateAppCacheStatus(AppCacheGroup::IDLE);
}
void AppCacheUpdateJob::StartUpdate(AppCacheHost* host,
}
// Notify host (if any) if already checking or downloading.
- AppCacheGroup::UpdateStatus update_status = group_->update_status();
+ AppCacheGroup::UpdateAppCacheStatus update_status = group_->update_status();
if (update_status == AppCacheGroup::CHECKING ||
update_status == AppCacheGroup::DOWNLOADING) {
if (host) {
- NotifySingleHost(host, CHECKING_EVENT);
+ NotifySingleHost(host, APPCACHE_CHECKING_EVENT);
if (update_status == AppCacheGroup::DOWNLOADING)
- NotifySingleHost(host, DOWNLOADING_EVENT);
+ NotifySingleHost(host, APPCACHE_DOWNLOADING_EVENT);
// Add to fetch list or an existing entry if already fetched.
if (!new_master_resource.is_empty()) {
}
// Begin update process for the group.
- group_->SetUpdateStatus(AppCacheGroup::CHECKING);
+ MadeProgress();
+ group_->SetUpdateAppCacheStatus(AppCacheGroup::CHECKING);
if (group_->HasCache()) {
update_type_ = UPGRADE_ATTEMPT;
- NotifyAllAssociatedHosts(CHECKING_EVENT);
+ NotifyAllAssociatedHosts(APPCACHE_CHECKING_EVENT);
} else {
update_type_ = CACHE_ATTEMPT;
DCHECK(host);
- NotifySingleHost(host, CHECKING_EVENT);
+ NotifySingleHost(host, APPCACHE_CHECKING_EVENT);
}
if (!new_master_resource.is_empty()) {
}
void AppCacheUpdateJob::HandleCacheFailure(
- const std::string& error_message,
- ResultType result) {
+ 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_message.empty());
+ DCHECK(!error_details.message.empty());
DCHECK(result != UPDATE_OK);
internal_state_ = CACHE_FAILURE;
+ LogHistogramStats(result, failed_resource_url);
CancelAllUrlFetches();
- CancelAllMasterEntryFetches(error_message);
- NotifyAllError(error_message);
+ CancelAllMasterEntryFetches(error_details);
+ NotifyAllError(error_details);
DiscardInprogressCache();
internal_state_ = COMPLETED;
- AppCacheHistograms::CountUpdateJobResult(
- result, manifest_url_.GetOrigin());
DeleteSoon(); // To unwind the stack prior to deletion.
}
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) {
ContinueHandleManifestFetchCompleted(false);
} else if ((response_code == 404 || response_code == 410) &&
update_type_ == UPGRADE_ATTEMPT) {
- storage_->MakeGroupObsolete(group_, this); // async
+ 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(message, fetcher->result());
+ 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) {
+ bool success,
+ int response_code) {
DCHECK(master_entry_fetches_.empty());
- CancelAllMasterEntryFetches("The cache has been made obsolete, "
- "the manifest file returned 404 or 410");
+ 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(OBSOLETE_EVENT);
+ NotifyAllAssociatedHosts(APPCACHE_OBSOLETE_EVENT);
internal_state_ = COMPLETED;
MaybeCompleteUpdate();
} else {
// Treat failure to mark group obsolete as a cache failure.
- HandleCacheFailure("Failed to mark the cache as obsolete", DB_ERROR);
+ HandleCacheFailure(AppCacheErrorDetails(
+ "Failed to mark the cache as obsolete",
+ APPCACHE_UNKNOWN_ERROR,
+ GURL(),
+ 0,
+ false /*is_cross_origin*/),
+ DB_ERROR,
+ GURL());
}
}
Manifest manifest;
if (!ParseManifest(manifest_url_, manifest_data_.data(),
- manifest_data_.length(), manifest)) {
+ 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(message, MANIFEST_ERROR);
+ HandleCacheFailure(
+ AppCacheErrorDetails(
+ message, APPCACHE_SIGNATURE_ERROR, GURL(), 0,
+ false /*is_cross_origin*/),
+ APPCACHE_MANIFEST_ERROR,
+ GURL());
VLOG(1) << message;
return;
}
}
}
- group_->SetUpdateStatus(AppCacheGroup::DOWNLOADING);
- NotifyAllAssociatedHosts(DOWNLOADING_EVENT);
+ 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
++url_fetches_completed_;
int response_code = request->status().is_success()
- ? request->GetResponseCode() : -1;
+ ? request->GetResponseCode()
+ : fetcher->redirect_response_code();
+
AppCacheEntry& entry = url_file_list_.find(url)->second;
if (response_code / 100 == 2) {
const char* kFormatString = "Resource fetch failed (%d) %s";
std::string message = FormatUrlErrorMessage(
kFormatString, url, fetcher->result(), response_code);
- HandleCacheFailure(message, fetcher->result());
+ 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) {
const char* kFormatString = "Manifest fetch failed (%d) %s";
std::string message = FormatUrlErrorMessage(
kFormatString, request->url(), fetcher->result(), response_code);
- host_notifier.SendErrorNotifications(message);
+ 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.
// Section 6.9.4, step 22.3.
if (update_type_ == CACHE_ATTEMPT && pending_master_entries_.empty()) {
- HandleCacheFailure(message, fetcher->result());
+ HandleCacheFailure(AppCacheErrorDetails(message,
+ appcache::APPCACHE_MANIFEST_ERROR,
+ request->url(),
+ response_code,
+ false /*is_cross_origin*/),
+ fetcher->result(),
+ GURL());
return;
}
}
<< " response code: " << response_code;
ScheduleUpdateRetry(kRerunDelayMs);
if (response_code == 200) {
- HandleCacheFailure("Manifest changed during update", MANIFEST_ERROR);
+ 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(message, fetcher->result());
+ HandleCacheFailure(AppCacheErrorDetails(message,
+ appcache::APPCACHE_MANIFEST_ERROR,
+ GURL(),
+ response_code,
+ false /*is_cross_origin*/),
+ fetcher->result(),
+ GURL());
}
}
}
base::Bind(&AppCacheUpdateJob::OnManifestDataWriteComplete,
base::Unretained(this)));
} else {
- HandleCacheFailure("Failed to write the manifest headers to storage",
- DISKCACHE_ERROR);
+ HandleCacheFailure(
+ AppCacheErrorDetails("Failed to write the manifest headers to storage",
+ APPCACHE_UNKNOWN_ERROR,
+ GURL(),
+ 0,
+ false /*is_cross_origin*/),
+ DISKCACHE_ERROR,
+ GURL());
}
}
duplicate_response_ids_.push_back(entry.response_id());
StoreGroupAndCache();
} else {
- HandleCacheFailure("Failed to write the manifest data to storage",
- DISKCACHE_ERROR);
+ HandleCacheFailure(
+ AppCacheErrorDetails("Failed to write the manifest data to storage",
+ APPCACHE_UNKNOWN_ERROR,
+ GURL(),
+ 0,
+ false /*is_cross_origin*/),
+ DISKCACHE_ERROR,
+ GURL());
}
}
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 = QUOTA_ERROR;
+ result = APPCACHE_QUOTA_ERROR;
+ reason = appcache::APPCACHE_QUOTA_ERROR;
}
- HandleCacheFailure(message, result);
+ HandleCacheFailure(
+ AppCacheErrorDetails(message, reason, GURL(), 0,
+ false /*is_cross_origin*/),
+ result,
+ GURL());
}
}
void AppCacheUpdateJob::NotifySingleHost(AppCacheHost* host,
- EventID event_id) {
+ AppCacheEventID event_id) {
std::vector<int> ids(1, host->host_id());
host->frontend()->OnEventRaised(ids, event_id);
}
-void AppCacheUpdateJob::NotifyAllAssociatedHosts(EventID event_id) {
+void AppCacheUpdateJob::NotifyAllAssociatedHosts(AppCacheEventID event_id) {
HostNotifier host_notifier;
AddAllAssociatedHostsToNotifier(&host_notifier);
host_notifier.SendNotifications(event_id);
NotifyAllProgress(GURL());
}
-void AppCacheUpdateJob::NotifyAllError(const std::string& error_message) {
+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.SendErrorNotifications(error_message);
+ host_notifier.SendLogMessage(message);
}
void AppCacheUpdateJob::AddAllAssociatedHostsToNotifier(
// http://code.google.com/p/chromium/issues/detail?id=95101
if (service_->storage() == storage_) {
// Use a local variable because service_ is reset in HandleCacheFailure.
- AppCacheService* service = service_;
- HandleCacheFailure("Manifest entry not found in existing cache",
- DB_ERROR);
+ 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());
}
}
void AppCacheUpdateJob::CancelAllMasterEntryFetches(
- const std::string& error_message) {
+ 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
master_entries_to_fetch_.erase(master_entries_to_fetch_.begin());
}
- host_notifier.SendErrorNotifications(error_message);
+ host_notifier.SendErrorNotifications(error_details);
}
bool AppCacheUpdateJob::MaybeLoadFromNewestCache(const GURL& url,
}
}
// 6.9.4 steps 7.3-7.7.
- NotifyAllAssociatedHosts(NO_UPDATE_EVENT);
+ NotifyAllAssociatedHosts(APPCACHE_NO_UPDATE_EVENT);
DiscardDuplicateResponses();
internal_state_ = COMPLETED;
break;
DCHECK(stored_state_ == STORED);
NotifyAllFinalProgress();
if (update_type_ == CACHE_ATTEMPT)
- NotifyAllAssociatedHosts(CACHED_EVENT);
+ NotifyAllAssociatedHosts(APPCACHE_CACHED_EVENT);
else
- NotifyAllAssociatedHosts(UPDATE_READY_EVENT);
+ NotifyAllAssociatedHosts(APPCACHE_UPDATE_READY_EVENT);
DiscardDuplicateResponses();
internal_state_ = COMPLETED;
- AppCacheHistograms::CountUpdateJobResult(
- UPDATE_OK, manifest_url_.GetOrigin());
+ LogHistogramStats(UPDATE_OK, GURL());
break;
case CACHE_FAILURE:
NOTREACHED(); // See HandleCacheFailure
void AppCacheUpdateJob::Cancel() {
internal_state_ = CANCELLED;
+ LogHistogramStats(CANCELLED_ERROR, GURL());
+
if (manifest_fetcher_) {
delete manifest_fetcher_;
manifest_fetcher_ = NULL;
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();
// 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_->SetUpdateStatus(AppCacheGroup::IDLE);
+ group_->SetUpdateAppCacheStatus(AppCacheGroup::IDLE);
group_ = NULL;
base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);