| Index: chrome/renderer/net/net_error_helper_core.cc
|
| diff --git a/chrome/renderer/net/net_error_helper_core.cc b/chrome/renderer/net/net_error_helper_core.cc
|
| index 55cb771c9e50b103835ff0cf013367e5c7c39bff..80dcb4889876778b625c688d0231d62e1bc1f420 100644
|
| --- a/chrome/renderer/net/net_error_helper_core.cc
|
| +++ b/chrome/renderer/net/net_error_helper_core.cc
|
| @@ -6,6 +6,9 @@
|
|
|
| #include <string>
|
|
|
| +#include "base/bind.h"
|
| +#include "base/callback.h"
|
| +#include "base/location.h"
|
| #include "base/metrics/histogram.h"
|
| #include "chrome/common/localized_error.h"
|
| #include "net/base/escape.h"
|
| @@ -16,6 +19,15 @@
|
|
|
| namespace {
|
|
|
| +base::TimeDelta GetAutoReloadTime(size_t reload_count) {
|
| + static const int kDelaysMs[] = {
|
| + 0, 5000, 30000, 60000, 300000, 600000, 1800000
|
| + };
|
| + if (reload_count >= arraysize(kDelaysMs))
|
| + reload_count = arraysize(kDelaysMs) - 1;
|
| + return base::TimeDelta::FromMilliseconds(kDelaysMs[reload_count]);
|
| +}
|
| +
|
| // Returns whether |net_error| is a DNS-related error (and therefore whether
|
| // the tab helper should start a DNS probe after receiving it.)
|
| bool IsDnsError(const blink::WebURLError& error) {
|
| @@ -24,6 +36,11 @@ bool IsDnsError(const blink::WebURLError& error) {
|
| error.reason == net::ERR_NAME_RESOLUTION_FAILED);
|
| }
|
|
|
| +bool IsReloadableError(const blink::WebURLError& error) {
|
| + return error.domain.utf8() == net::kErrorDomain &&
|
| + error.reason != net::ERR_ABORTED;
|
| +}
|
| +
|
| // If an alternate error page should be retrieved remotely for a main frame load
|
| // that failed with |error|, returns true and sets |error_page_url| to the URL
|
| // of the remote error page.
|
| @@ -116,23 +133,44 @@ struct NetErrorHelperCore::ErrorPageInfo {
|
| bool is_finished_loading;
|
| };
|
|
|
| -NetErrorHelperCore::NetErrorHelperCore(Delegate* delegate)
|
| +NetErrorHelperCore::NetErrorHelperCore(Delegate* delegate,
|
| + scoped_ptr<MockableOneShotTimer> reload_timer)
|
| : delegate_(delegate),
|
| - last_probe_status_(chrome_common_net::DNS_PROBE_POSSIBLE) {
|
| + last_probe_status_(chrome_common_net::DNS_PROBE_POSSIBLE),
|
| + auto_reload_enabled_(false),
|
| + auto_reload_timer_(reload_timer.Pass()),
|
| + auto_reload_count_(0),
|
| + auto_reload_pending_(false),
|
| + online_(true) {
|
| }
|
|
|
| NetErrorHelperCore::~NetErrorHelperCore() {
|
| }
|
|
|
| -void NetErrorHelperCore::OnStop() {
|
| - // On stop, cancel loading the alternate error page, and prevent any pending
|
| - // error page load from starting a new error page load. Swapping in the error
|
| - // page when it's finished loading could abort the navigation, otherwise.
|
| - if (committed_error_page_info_)
|
| +void NetErrorHelperCore::CancelPendingFetches() {
|
| + // Cancel loading the alternate error page, and prevent any pending error page
|
| + // load from starting a new error page load. Swapping in the error page when
|
| + // it's finished loading could abort the navigation, otherwise.
|
| + if (committed_error_page_info_) {
|
| committed_error_page_info_->alternate_error_page_url = GURL();
|
| + }
|
| if (pending_error_page_info_)
|
| pending_error_page_info_->alternate_error_page_url = GURL();
|
| delegate_->CancelFetchErrorPage();
|
| + auto_reload_timer_->Stop();
|
| +}
|
| +
|
| +void NetErrorHelperCore::OnStop() {
|
| + CancelPendingFetches();
|
| + if (auto_reload_pending_) {
|
| + HISTOGRAM_CUSTOM_ENUMERATION("Net.AutoReload.ErrorAtStop",
|
| + committed_error_page_info_->error.reason,
|
| + net::GetAllErrorCodesForUma());
|
| + HISTOGRAM_COUNTS("Net.AutoReload.CountAtStop",
|
| + auto_reload_count_);
|
| + }
|
| + auto_reload_count_ = 0;
|
| + auto_reload_pending_ = false;
|
| }
|
|
|
| void NetErrorHelperCore::OnStartLoad(FrameType frame_type, PageType page_type) {
|
| @@ -142,7 +180,7 @@ void NetErrorHelperCore::OnStartLoad(FrameType frame_type, PageType page_type) {
|
| // If there's no pending error page information associated with the page load,
|
| // or the new page is not an error page, then reset pending error page state.
|
| if (!pending_error_page_info_ || page_type != ERROR_PAGE) {
|
| - OnStop();
|
| + CancelPendingFetches();
|
| }
|
| }
|
|
|
| @@ -150,12 +188,32 @@ void NetErrorHelperCore::OnCommitLoad(FrameType frame_type) {
|
| if (frame_type != MAIN_FRAME)
|
| return;
|
|
|
| + if (committed_error_page_info_ && !pending_error_page_info_ &&
|
| + auto_reload_pending_) {
|
| + int reason = committed_error_page_info_->error.reason;
|
| + HISTOGRAM_CUSTOM_ENUMERATION("Net.AutoReload.ErrorAtSuccess",
|
| + reason,
|
| + net::GetAllErrorCodesForUma());
|
| + if (auto_reload_count_ == 1)
|
| + HISTOGRAM_CUSTOM_ENUMERATION("Net.AutoReload.ErrorAtFirstSuccess",
|
| + reason,
|
| + net::GetAllErrorCodesForUma());
|
| + }
|
| +
|
| committed_error_page_info_.reset(pending_error_page_info_.release());
|
| }
|
|
|
| void NetErrorHelperCore::OnFinishLoad(FrameType frame_type) {
|
| - if (frame_type != MAIN_FRAME || !committed_error_page_info_)
|
| + if (frame_type != MAIN_FRAME)
|
| + return;
|
| +
|
| + if (!committed_error_page_info_) {
|
| + if (auto_reload_pending_)
|
| + HISTOGRAM_COUNTS("Net.AutoReload.CountAtSuccess", auto_reload_count_);
|
| + auto_reload_count_ = 0;
|
| + auto_reload_pending_ = false;
|
| return;
|
| + }
|
|
|
| committed_error_page_info_->is_finished_loading = true;
|
|
|
| @@ -167,6 +225,9 @@ void NetErrorHelperCore::OnFinishLoad(FrameType frame_type) {
|
| GURL error_page_url;
|
| delegate_->FetchErrorPage(
|
| committed_error_page_info_->alternate_error_page_url);
|
| + } else if (auto_reload_enabled_) {
|
| + auto_reload_pending_ = true;
|
| + MaybeStartAutoReloadTimer();
|
| }
|
|
|
| if (!committed_error_page_info_->needs_dns_updates ||
|
| @@ -316,3 +377,85 @@ blink::WebURLError NetErrorHelperCore::GetUpdatedError(
|
|
|
| return updated_error;
|
| }
|
| +
|
| +void NetErrorHelperCore::Reload() {
|
| + if (!committed_error_page_info_) {
|
| + return;
|
| + }
|
| + delegate_->ReloadPage();
|
| +}
|
| +
|
| +bool NetErrorHelperCore::MaybeStartAutoReloadTimer() {
|
| + if (!committed_error_page_info_)
|
| + return false;
|
| +
|
| + if (!IsReloadableError(committed_error_page_info_->error))
|
| + return false;
|
| +
|
| + if (committed_error_page_info_->was_failed_post)
|
| + return false;
|
| +
|
| + if (!online_)
|
| + return false;
|
| +
|
| + if (!auto_reload_pending_)
|
| + return false;
|
| +
|
| + StartAutoReloadTimer();
|
| + return true;
|
| +}
|
| +
|
| +void NetErrorHelperCore::StartAutoReloadTimer() {
|
| + base::TimeDelta delay = GetAutoReloadTime(auto_reload_count_);
|
| + auto_reload_count_++;
|
| + auto_reload_timer_->Stop();
|
| + auto_reload_timer_->Start(FROM_HERE, delay,
|
| + base::Bind(&NetErrorHelperCore::Reload,
|
| + base::Unretained(this)));
|
| +}
|
| +
|
| +// Handler for NetworkStateChanged notification from the browser process. If the
|
| +// network state changes to online, this method is responsible for starting the
|
| +// auto-reload process.
|
| +//
|
| +// Warning: if there are many tabs sitting at an error page, this handler will
|
| +// be run at the same time for each of their top-level renderframes, which can
|
| +// cause many requests to be started at the same time. There's no current
|
| +// protection against this kind of "reload storm".
|
| +//
|
| +// TODO(rdsmith): prevent the reload storm.
|
| +void NetErrorHelperCore::NetworkStateChanged(bool online) {
|
| + online_ = online;
|
| + if (auto_reload_timer_->IsRunning()) {
|
| + // If there's an existing timer running, stop it and reset the retry count.
|
| + auto_reload_timer_->Stop();
|
| + auto_reload_count_ = 0;
|
| + }
|
| +
|
| + if (online)
|
| + // If we just got online, maybe start auto-reloading again.
|
| + MaybeStartAutoReloadTimer();
|
| +}
|
| +
|
| +bool NetErrorHelperCore::ShouldSuppressErrorPage(const GURL& url) {
|
| + // Don't suppress errors if not auto-reloading.
|
| + if (!auto_reload_pending_)
|
| + return false;
|
| +
|
| + // If auto_reload_timer_ is still running, this error page isn't from an auto
|
| + // reload.
|
| + if (auto_reload_timer_->IsRunning())
|
| + return false;
|
| +
|
| + // If there's no committed error page, this error page wasn't from an auto
|
| + // reload.
|
| + if (!committed_error_page_info_)
|
| + return false;
|
| +
|
| + GURL error_url = committed_error_page_info_->error.unreachableURL;
|
| + if (error_url != url)
|
| + return false;
|
| +
|
| + MaybeStartAutoReloadTimer();
|
| + return true;
|
| +}
|
|
|